Manjushaka / blog

3 stars 0 forks source link

webpack guide #39

Open Manjushaka opened 5 years ago

Manjushaka commented 5 years ago

学习部署工具webpack的使用:

  1. By stating what dependencies a module needs, webpack can use this information to build a dependency graph. It then uses the graph to generate an optimized bundle where scripts will be executed in the correct order.
  2. Custom parameters can be passed to webpack by adding two dashes between the npm run build command and your parameters, e.g. npm run build -- --colors
  3. Prior to webpack, front-end developers would use tools like grunt and gulp to process these assets and move them from their /src folder into their /dist or /build directory. The same idea was used for JavaScript modules, but tools like webpack will dynamically bundle all dependencies (creating what's known as a dependency graph). This is great because every module now explicitly states its dependencies and we'll avoid bundling modules that aren't in use.
Manjushaka commented 5 years ago

Asset Management: 1) Loading css:

          {
              test: /\.css$/,
              use: [
                  'style-loader',
                  'css-loader'
              ]
          },
2) loading images:
          {
              test: /\.(png|svg|jpg|jpeg|gif)$/,
              use: [
                  'file-loader'
              ]
          }

提升image:Check out the image-webpack-loader and url-loader for more on how you can enhance your image loading process. 3) loading font:

          {
              test: /\.(woff|woff2|eot|ttf|otf)$/,
              use: [
                  'file-loader'
              ]
          }

在css中引入字体的时候要注意: @font-face { font-family: 'MyFont'; src: url('./font3.ttf') format('truetype'); // 不同格式的字体,format的值不一样,不一定跟文件后缀一致,如果写错,会出错,浏览器下载不到对应的字体文件 font-weight: 600; font-style: normal; }

src: url('./my-font.woff2') format('woff2'), url('./my-font.woff') format('woff');

4) loading data(json, csv, tsv, xml)
          {
              test: /\.(csv|tsv)$/,
              use: [
                  'csv-loader'
              ]
          },
          {
              test: /\.xml$/,
              use: [
                  'xml-loader'
              ]
          }
Manjushaka commented 5 years ago

Output Management: 可以设置多个输入输出:

  entry: {
      app: './src/index.js',
      print: './src/print.js'
  },
  output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, 'dist')
  },

1)每次都手动修改index.html引入bundle就不行了,可以用HtmlWebpackPlugin r来实现自动创建index.html. 同时html-webpack-template可以提供更多的特性。 npm i -D html-webpack-plugin

  plugins: [
      new HtmlWebpackPlugin({
          title: 'Output Management'
      })
  ],

2)清理/dist目录:clean-webpack-plugin npm i -D clean-webpack-plugin

  plugins: [
      new CleanWebpackPlugin(),
      new HtmlWebpackPlugin({
          title: 'Output Management'
      })
  ],
Manjushaka commented 5 years ago

Development: 1) using source maps:跟踪错误,指明具体的原文件,而不是打包后的文件。

devtool: 'inline-source-map', // 只在开发环境有用

2)development tool: webpack’s Watch mode: 监测文件的变化,自动编译,但并不会自动刷新浏览器

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "watch": "webpack --watch", // nom run watch
    "build": "webpack"
  },

webpack-dev-server: npm i -D webpack-dev-server

  devServer: {
      contentBase: './dist',
  },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "watch": "webpack --watch",
    "start": "webpack-dev-server --open", // 启动服务器
    "build": "webpack"
  },

webpack-dev-middleware: npm i -D webpack-dev-middleware express

  output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, 'dist'),
    publicPath: '/'
  },
  Server.js
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "watch": "webpack --watch",
    "start": "webpack-dev-server --open",
    "server": "node server.js", // 启动服务器
    "build": "webpack"
  },

webpack-dev-server内部使用的就是webpack-dev-middleware

Manjushaka commented 5 years ago

Hot Module Replacement, HMR(开发环境使用) 1)webpack-dev-server:

const webpack = require('webpack');
  devServer: {
      contentBase: './dist',
      hot: true,
  },
  plugins: [
      new CleanWebpackPlugin(),
      new HtmlWebpackPlugin({
          title: 'Development'
      }),
      new webpack.HotModuleReplacementPlugin()
  ],

只是这样子设置(只是设置了 devServer: { contentBase: './dist', }也一样),会每次完全重新刷新页面,重新拉取所有的资源。 加上

    if (module.hot) {
        module.hot.accept('./print.js', function() {
          console.log('Accepting the updated printMe module!');
          // printMe();
          document.body.removeChild(element);
          element = component();
          document.body.appendChild(element);
        })
      }

当修改printMe的时候,会更新printMe绑定的函数,然后页面上的btn.onclick = printMe;也重新绑定新的printMe。但是会很麻烦(每个更新的文件都需要重新绑定),可以使用插件简化这个绑定的过程。

webpack输出真实的文件,而webpack-dev-server输出的文件只存在于内存中,不输出真实的文件

从外部角度看——自动刷新 当我们对业务代码做了一些修改然后保存后(command+s),页面会自动刷新,我们所做的修改会直接同步到页面上,而不需要我们刷新页面,或重新开启服务。从内部角度看——模块热替换 在热替换(HMR)机制里,不是重载整个页面,HMR程序会只加载被更新的那一部分模块,然后将其注入到运行中的APP中。

a. HMR with Stylesheets: style-loader:

  module: {
      rules: [
          {
              test: /\.css$/,
              use: [
                  'style-loader',
                  'css-loader'
              ]
          }
      ]
  }

b. React Hot Loader: Tweak react components in real time. 2)webpack-dev-middleware: 使用webpack-hot-middleware

Manjushaka commented 5 years ago

Code Splitting:将代码分割成多个bundles,实现按需加载或并行下载,优化加载速度。

  function getComponent() {
    return import(/* webpackChunkName: "lodash_dan" */ 'lodash').then(({ default: _ }) => {
      var element = document.createElement('div');

      element.innerHTML = _.join(['Hello', 'webpack'], ' ');

      return element;

    }).catch(error => 'An error occurred while loading the component');
  }

  getComponent().then(component => {
    document.body.appendChild(component);
  })

      async function getComponent() {
        var element = document.createElement('div');
        const { default: _ } = await import(/* webpackChunkName: "lodash" */ 'lodash');

        element.innerHTML = _.join(['Hello', 'webpack'], ' ');

        return element;
      }

      getComponent().then(component => {
        document.body.appendChild(component);
      });

Prefetching / Preloading modules

Bundle Analysis: 命令:webpack --profile --json > stats.json npm install -D webpack-bundle-analyzer https://github.com/webpack-contrib/webpack-bundle-analyzer 五彩斑斓的分析工具

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
  plugins: [
      new CleanWebpackPlugin(),
      new HtmlWebpackPlugin({
          title: 'Development'
      }),
      new webpack.HotModuleReplacementPlugin(),
      new BundleAnalyzerPlugin({
        generateStatsFile: true
      })
  ],

Further Reading

b. require.ensure:

Manjushaka commented 5 years ago

Lazy Loading(on demand) 需要的时候才去加载,加速了应用的初始化加载,可能有些模块永远不会加载减轻了大小。

button.onclick = e => import(/* webpackChunkName: "print" */ './print').then(module => {
      var print = module.default;

      print('sandy');
    });

React: Code Splitting and Lazy Loading https://dzone.com/articles/lazy-loading-es2015-modules-in-the-browser

Manjushaka commented 5 years ago

caching https://searchstorage.techtarget.com/definition/cache 1) output filenames output.filename: [name] [hash] [chunkhash] [contenthash] 注意点:a.[chunkhash] [contenthash]主要用于生产环境,不能和HMR同时使用(HMR用于开发环境)。区别?? [contenthash]改变js,hash值变化;改变js引入的css,hash值变化。不改变任何文件重新build,hash值不便 [chunkhash]改变js,hash值变化;改变js引入的css,hash值变化。不改变任何文件重新build,hash值不便 2)Extracting Boilerplate

  optimization: {
      runtimeChunk: 'single',
      splitChunks: {
          cacheGroups: {
              vendor: {
                  test: /[\\/]node_modules[\\/]/,
                  name: 'verdors',
                  chunks: 'all'
              }
          }
      }
  },

webpack provides an optimization feature to split runtime code into a separate chunk using the optimization.runtimeChunk option. Set it to single to create a single runtime bundle for all chunks:???

只是这样子cache,每次build之后公共模块的hash也会改变,所以要让公共模块不变。 A. 开发环境:NamedModulesPlugin,可以获得有用的输出,但耗时。 B. 生产环境:HashedModuleIdsPlugin

   plugins: [
      new CleanWebpackPlugin(),
      new HtmlWebpackPlugin({
          title: 'caching'
      }),
      new webpack.HashedModuleIdsPlugin()
  ],
Manjushaka commented 5 years ago
  1. Authoring Libraries 打包library

  2. Environment Variables ???没看懂啥意思

  3. Build Performance

  4. Content Security Policies

  5. Development-Vagrant 虚拟部署??

  6. Dependency Management es6 modules commonjs amd

  7. Installation

  8. Scaffolding 脚手架???

  9. Tree Shaking Import export 抖掉无用的代码

  10. Production process.env.NODE_ENV在webpack config里面是undefined,在运行的脚本里可以获得development/production。

Manjushaka commented 5 years ago

Production process.env.NODE_ENV在webpack config里面是undefined,在运行的脚本里可以获得development/production。例如,index.js:

  console.log('index', process.env.NODE_ENV);

  if (process.env.NODE_ENV !== 'production') {
      console.log('looks like we are in development mode!')
  }

将 webpack.config.js分成三个webpack.common.js webpack.dev.js webpack.prod.js webpack.common.js:

const path = require('path');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    entry: {
        app: './src/index.js'
    },
    plugins: [
        new CleanWebpackPlugin(),
        new HtmlWebpackPlugin({
        title: 'Production'
        })
    ],
    output: {
        filename: '[name].bundle.js',
        path: path.resolve(__dirname, 'dist')
    }
};

webpack.dev.js:

const merge = require('webpack-merge');
const common = require('./webpack.common.js');

module.exports = merge(common, {
    mode: 'development', // automatically configures DefinePlugin
    devtool: 'inline-source-map', // strong source mapping
    devServer: {
        contentBase: './dist'
    }
});

webpack.prod.js:

const merge = require('webpack-merge');
const common = require('./webpack.common.js');

module.exports = merge(common, {
    mode: 'production', // loads TerserPlugin
    devtool: 'source-map' // Avoid inline-*** and eval-*** use in production as they can increase bundle size and reduce the overall performance.
});

package.json:

  "scripts": {
    "start": "webpack-dev-server --open --config webpack.dev.js",
    "build": "webpack --config webpack.prod.js"
  },
Manjushaka commented 5 years ago

shimming 使用全局变量的地方 或只在需要polyfill的浏览器按需加载对应的polyfill 使用ProvidePlugin插件,然后就可以全局使用_代替lodash了:

   plugins: [
     new webpack.ProvidePlugin({
       _: 'lodash',
     })
   ]

加载需要的polyfills:

    entry: {
    polyfills: './src/polyfills.js',
    index: './src/index.js'
   },

       <script>
       var modernBrowser = (
         'fetch' in window &&
         'assign' in Object
       );

       if ( !modernBrowser ) {
         var scriptElement = document.createElement('script');

         scriptElement.async = false;
         scriptElement.src = '/polyfills.bundle.js';
         document.head.appendChild(scriptElement);
       }
     </script>