js构建工具

前一段时间搭建Isomorphic过程中,对js的构建工具接触比较多,也做了一些记录但都不够系统,这里对接触的构建工具做一些较系统的整理,包括:webpack、gulp、babel

webpack

  • webpack的用途
    webpack是一个打包工具,打包又是为什么呢?对于前端,用react写了很多组件,如果直接编译成js会有很多的文件,也就需要很多的script来做引用,这样会极为不方便,webpack就是自动的寻找依赖关系,将这些文件打包到一起的工具。
    起初它只做这个,这样对于非js的文件就不太好处理,这样它通过自己的插件(loader)来对这些非js文件进行支持。这样对于css、ts、stylus等文件,都可以通过webpack来进行处理了。

  • webpack基本概念

    原文
    4大概念:Entry、Output、Loaders、Plugins

    • Entry:
      An entry point indicates which module webpack should use to begin building out its internal dependency graph. webpack will figure out which other modules and libraries that entry point depends on

    • Output:
      The output property tells webpack where to emit the bundles it creates and how to name these files

    • Loaders:
      Out of the box, webpack only understands JavaScript and JSON files. Loaders allow webpack to process other types of files and convert them into valid modules that can be consumed by your application and added to the dependency graph.

    • Plugins:
      While loaders are used to transform certain types of modules, plugins can be leveraged to perform a wider range of tasks like bundle optimization, asset management and injection of environment variables.

  • 使用
    对于Ts与stylus的loader配置如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    module: {
    rules: [
    {
    test: /\.tsx$/,
    use: ['awesome-typescript-loader'],
    exclude: /node_modules/
    },
    {
    test: /\.styl$/,
    use: [
    {
    loader: 'style-loader'
    },
    {
    loader: 'css-loader',
    options: {
    modules: true
    }
    },
    {
    loader: 'stylus-loader'
    }
    ],
    exclude: /node_modules/
    }
    ]
    }
  • 其他
    webpack像是一个家族,对于同构它们提供webpack-isomorphic-tools,但在gitlab库的说明上: It allowed many projects to set up basic isomorphic (universal) rendering in the early days but is now considered deprecated and new projects shouldn’t use it. This library can still be found in legacy projects. For new projects use either universal-webpack or all-in-one frameworks like Next.js.

gulp

  • gulp的用途
    gulp是一个流式任务运行器,换句话说,我们通过命令行执行的操作,可以通过gulp定义成任务,然后对任务进行编排(指定运行顺序)、运行。
    gulp的任务执行,从触发方式上看可以是手动执行,也可以通过watch检测文件变化自动执行。

  • gulp基本概念
    原文
    输出(Emits)符合所提供的匹配模式(glob)或者匹配模式的数组(array of globs)的文件。 将返回一个 Vinyl files 的 stream 它可以被 piped 到别的插件中。

    • pipe
      管道,任务流

      1
      2
      3
      4
      gulp.src('client/templates/*.jade')
      .pipe(jade())
      .pipe(minify())
      .pipe(gulp.dest('build/minified_templates'));
    • task
      这里采用的是函数编程范式

      1
      2
      3
      4
      5
      6
      gulp.task('somename', function() {
      var stream = gulp.src('client/**/*.js')
      .pipe(minify())
      .pipe(gulp.dest('build'));
      return stream;
      });

      任务编排:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      var gulp = require('gulp');

      // 返回一个 callback,因此系统可以知道它什么时候完成
      gulp.task('one', function(cb) {
      // 做一些事 -- 异步的或者其他的
      cb(err); // 如果 err 不是 null 或 undefined,则会停止执行,且注意,这样代表执行失败了
      });

      // 定义一个所依赖的 task 必须在这个 task 执行之前完成
      gulp.task('two', ['one'], function() {
      // 'one' 完成后
      });

      gulp.task('default', ['one', 'two']);
  • watch
    监视文件变化

    1
    2
    3
    4
    var watcher = gulp.watch('js/**/*.js', ['uglify','reload']);
    watcher.on('change', function(event) {
    console.log('File ' + event.path + ' was ' + event.type + ', running tasks...');
    });
  • 其他
    gulp其实也提供了一些插件,比如gulp-typescript,可以Ts直接编译成js,它与webpack中的ts-loader应该有类似的作用,只不过webpack中将所有的文件都打包到一起了。

babel

  • babel的用途
    babel是转码器,这个转码器是将不同版本的js进行转换。目前有的浏览器并不支持 ES6,而我们又用ES6来编码,那就用babel进行处理,自动转换成相应的版本。
    而且babel支持React的JSX语法转换,对于Ts,babel也有所支持,比如退去所有的annotation,以及类型

  • 使用
    官方文档
    babel部分摘自:阮一峰:Babel 入门教程

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    # 转码结果输出到标准输出
    $ babel example.js

    # 转码一个文件:--out-file 或 -o 参数指定输出文件
    $ babel example.js --out-file compiled.js
    # 或
    $ babel example.js -o compiled.js

    # 转码整个目录: --out-dir 或 -d 参数指定输出目录
    $ babel src --out-dir lib
    # 或
    $ babel src -d lib

    # -s 参数生成source map文件
    $ babel src -d lib -s
  • babel全家桶

    • babel-cli
      上文使用cli运行,需要安装babel-cli:npm install --global babel-cli

    • babel-core
      我们在用的使用更多的是通过gulp来调用,就需要babel-core,npm install babel-core --save

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      var babel = require('babel-core');

      // 字符串转码
      babel.transform('code();', options);
      // => { code, map, ast }

      // 文件转码(异步)
      babel.transformFile('filename.js', options, function(err, result) {
      result; // => { code, map, ast }
      });

      // 文件转码(同步)
      babel.transformFileSync('filename.js', options);
      // => { code, map, ast }
    • babel-polyfill
      Babel默认只转换新的JavaScript句法(syntax),而不转换新的API,比如Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise等全局对象,以及一些定义在全局对象上的方法(比如Object.assign)都不会转码。 在使用上,就需要babel-ployfill来完成对这些内容的支持。

      安装:
      npm install --save babel-polyfill

      在脚本的头部,加入:
      import 'babel-polyfill';

webpack-isomorphic-tools

  • 前言
    在搭建同构应用中,几次都遇到这个工具,在这里对这个工具进行一个整理

  • webpack-isomorphic-tools的用途
    webpack做的事情,是将文件进行打包,方便浏览器去获取原本这些分散的js,但对于isomorphic,事情变的不同。node.js的后端并不需要webpack打包,而不经过打包,对于有些类型的文件(图片类、CSS类),后端没法直接支持。这也正是在前面SSR、CSR与同构中对stylus文件处理时遇到的问题。

    官网

  • 使用

    • 安装
      npm install webpack-isomorphic-tools –save

    • webpack-isomorphic-tools嵌入webpack的配置中

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      var WebpackIsomorphicToolsPlugin = require('webpack-isomorphic-tools/plugin')

      var webpackIsomorphicToolsPlugin =
      // webpack-isomorphic-tools settings reside in a separate .js file
      // (because they will be used in the web server code too).
      new WebpackIsomorphicToolsPlugin(require('./webpack-isomorphic-tools-configuration'))
      // also enter development mode since it's a development webpack configuration
      // (see below for explanation)
      .development()

      // usual Webpack configuration
      module.exports =
      {
      context: '(required) your project path here',

      module:
      {
      loaders:
      [
      ...,
      {
      test: webpackIsomorphicToolsPlugin.regularExpression('images'),
      loader: 'url-loader?limit=10240', // any image below or equal to 10K will be converted to inline base64 instead
      }
      ]
      },

      plugins:
      [
      ...,

      webpackIsomorphicToolsPlugin
      ]
      ...
      }

      官网在这里也说明了一下为什么要.development()。对于开发模式,它让asset缓存失效,而让asset hot reload使能。

    • isomorphic自己的配置文件:webpack-isomorphic-tools-configuration.js

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      import WebpackIsomorphicToolsPlugin from 'webpack-isomorphic-tools/plugin'

      export default
      {
      assets:
      {
      images:
      {
      extensions: ['png', 'jpg', 'gif', 'ico', 'svg']
      }
      }
      }
    • 服务端运行入口 main.js配置

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      var WebpackIsomorphicTools = require('webpack-isomorphic-tools')

      // this must be equal to your Webpack configuration "context" parameter
      var projectBasePath = require('path').resolve(__dirname, '..')

      // this global variable will be used later in express middleware
      global.webpackIsomorphicTools = new WebpackIsomorphicTools(require('./webpack-isomorphic-tools-configuration') )
      .server(projectBasePath, function()
      {
      // webpack-isomorphic-tools is all set now.
      // here goes all your web application code:
      require('./server')
      })
    • 后端渲染页面

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      // clear require() cache if in development mode
      // (makes asset hot reloading work)
      if (process.env.NODE_ENV !== 'production')
      {
      webpackIsomorphicTools.refresh()
      }

      // for react-router example of determining current page by URL take a look at this:
      const pageComponent = [determine your page component here using request.path]

      // render the page to string and send it to the browser as text/html
      response.send('<!doctype html>\n' +
      React.renderToString(<Html assets={webpackIsomorphicTools.assets()} component={pageComponent} />))

      这里它传了fluxstore,在我们那并没有引入,故这个可以不用

    • Html中的使用
      Html中接收传入的assets const { assets, component, store } = this.props
      然后从asset中去获取资源,即可

      1
      2
      const picture = require('../assets/images/cat.jpg')
      const icon = require('../assets/images/icon/32x32.png')

      在header中,也可有直接去引入styles

      1
      2
      3
      4
      5
      6
      7
      {/* styles (will be present only in production with webpack extract text plugin) */}
      {Object.keys(assets.styles).map((style, i) =>
      <link href={assets.styles[style]} key={i} media="screen, projection"
      rel="stylesheet" type="text/css"/>)}

      {/* resolves the initial style flash (flicker) on page load in development mode */}
      { Object.keys(assets.styles).length === 0 ? <style dangerouslySetInnerHTML={{__html: require('../assets/styles/main_style.css')}}/> : null }

      asserts是由webpack-isomorphic-tools创建的webpack-asset.json中的内容

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      {
      "javascript":
      {
      "main": "/assets/main-d8c29e9b2a4623f696e8.js"
      },

      "styles":
      {
      "main": "/assets/main-d8c29e9b2a4623f696e8.css"
      },

      "assets":
      {
      "./assets/images/cat.jpg": "http://localhost:3001/assets/9059f094ddb49c2b0fa6a254a6ebf2ad.jpg"
      }

yarn vs npm

原文

源头是一致的,yarn会比npm快一些,并且安装版本统计

npm install === yarn
npm install taco –save === yarn add taco
npm uninstall taco –save === yarn remove taco
npm install taco –save-dev === yarn add taco –dev
npm update –save === yarn upgrade

推荐

推荐阅读:思过崖