• 【webpack 系列】基础篇


    Webpack 基础篇

    基本概念

    Webpack 是一个现代 JavaScript 应用程序的静态模块打包器。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图,其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle

    四个核心概念

    1. 入口(Entry): 构建 Webpack 内部依赖图的入口模块
    2. 输出(Output): 输出 Webpack 打包好的 Bundles
    3. Loader: 加载器,Webpack 原生只能解析 JavaScript 文件,Loaderwebpack 拥有了加载和解析非 JavaScript 文件的能力。
    4. 插件(Plugins): 扩展 Webpack 的功能,让 Webpack 具有更多的灵活性。在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。

    Webpack 基础配置

    本文所有代码可以查看github

    初始化项目

    新建一个文件夹 webpack-demo,在该目录中使用 npm init -y 进行项目初始化。

    mkdir webpack-demo && cd webpack-demo
    npm init -y
    

    运行以下命令安装最新版本或特定版本

    npm i -D webpack
    npm i -D webpack@<version>
    

    如果你使用 webpack 4+ 版本,你还需要安装 CLI

    npm i -D webpack-cli
    
    • npm i -Dnpm install --save-dev 的缩写,安装一个用于开发环境的安装包
    • npm i -Snpm install --save 的缩写,安装一个要打包到生产环境的安装包

    现在安装的 webpack 版本号是:

    ├── webpack@4.42.1
    └── webpack-cli@3.3.11
    

    新建 src/index.js 文件:

    // src/index.js
    
    class HelloComponent {
      constructor (content = '') {
        this.content = content;
        this.render();
      }
      render () {
        const element = document.createElement('div');
        element.innerHTML = this.content;
        document.body.appendChild(element);
      }
    }
    
    new HelloComponent('hello webpack');
    

    现在可以直接执行 npx webpack,默认是 production 模式。
    也可以在 package.json 中的 scripts 里配置一个 build 命令,模式指定为 production
    webpack 默认会将 ./src/index.js 作为入口文件,默认打包到 dist/main.js

    // ...
    "scripts": {
      "build": "webpack --mode=production"
    }
    // ...
    

    通过 npm run build 可以执行我们定义的命令,这是可以多了 dist/main.js 文件,这就是打包之后的 js 代码。

    webpack 配置文件

    上面例子中使用的是 webpack 的默认配置,下面我们来定义更加丰富的自定义配置。

    根目录下新建 webpack.config.js 文件

    const path = require('path');
    
    module.exports = {
      mode: 'development', // 模式
      entry: path.resolve(__dirname, 'src/index.js'), // 入口文件
      output: {
        path: path.resolve(__dirname, 'dist'), // 输出目录
        filename: 'bundle.js' // 输出文件名
      }
    }
    

    更改我们的 build 命令,指定 webpack 按照我们的配置文件来打包文件

    "scripts": {
      "build": "webpack --config webpack.config.js"
    }
    

    执行 npm run build 可以看到,dist 目录下新增了 bundle.js 文件。并且 bundle.js 是在开发模式下打包的,可以看到更多的信息。

    html-webpack-plugin 插件

    现在我们已经有了打包好的 js 文件了,需要添加个 html 文件来引入这个 js 文件在浏览器查看效果了。

    在实际开发中,为了避免每次修改打包的 js 文件被浏览器缓存而看不到最新的代码,我们会给打包文件加上 hash,相当于这个文件的版本号。这样每次修改后打包的 js 文件名都会不同,如果人工去修改 html 中的 js 文件名就太麻烦了,我们可以借助 html-webpack-plugin 插件来自动完成这些事情。

    安装 html-webpack-plugin

    npm i -D html-webpack-plugin
    

    新建 public/index.html 文件,修改我们的 webpack.config.js

    // webpack.config.js
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    
    module.exports = {
      //...
      plugins: [
        new HtmlWebpackPlugin({
          template: path.resolve(__dirname, 'public/index.html'), // 指定模板文件,不指定会生成默认的 index.html 文件
          filename: 'index.html' // 打包后的文件名
        })
      ]
    }
    

    执行 npm run build 可以看到 dist 目录下新增了 index.html 文件,并且自动将打包好的 bundle.js 文件通过 script 标签引入了。

    webpack-dev-server 开发工具

    现在可以通过浏览器直接打开 dist/index.html 查看了,但是这样每次改完代码我们都需要手动 npm run build 一下,这样太麻烦了。

    我们可以借助 webpack-dev-server 来解决这个问题。webpack-dev-server 会提供了一个简单的 web 服务器,并且能够实时重新加载。

    安装 webpack-dev-server

    npm i -D webpack-dev-server
    

    修改 package.json 文件

    // package.json
    "scripts": {
      "dev": "webpack-dev-server --config webpack.config.js",
      "build": "webpack --config webpack.config.js"
    }
    

    npm run dev 之后,默认会在 localhost:8080 下建立服务,通过访问这个地址可以访问到 dist 目录下的文件。

    可以在 webpack.config.jsdevServer 进行配置

    // webpack.config.js
    
    module.exports = {
      // ...
      devServer: {
        contentBase: path.join(__dirname, 'dist'),
        port: '9000', // 指定端口,默认是8080
        compress: true // 是否启用 gzip 压缩
      }
      //...
    }
    

    关于 webpack-dev-server 更多的配置可以点击查看

    mode

    我们在 package.json 定义了两条命令,但是 mode 都为 development。我们可以通过设置 process.env.NODE_ENV 的值来区分开发还是生产环境。

    我们需要安装一下 cross-env, 来实现跨平台设置 NODE_ENV

    npm i -D cross-env
    
    // package.json
    "scripts": {
      "dev": "cross-env NODE_ENV=development webpack-dev-server --config webpack.config.js",
      "build": "cross-env NODE_ENV=production webpack --config webpack.config.js"
    }
    

    修改 webpack.config.js

    // webpack.config.js
    
    const isProduction = process.env.NODE_ENV == 'production';
    
    module.exports = {
      mode: isProduction ? 'production' : 'development', // 模式
      // ...
    }
    

    设置 mode 的不同值可以启用相应模式下的 webpack 内置的优化

    选项 描述
    development 会将 process.env.NODE_ENV 的值设为 development。启用 NamedChunksPluginNamedModulesPlugin
    production 会将 process.env.NODE_ENV 的值设为 production。启用 FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPluginUglifyJsPlugin.

    用 babel 向后兼容 js 语法

    现在我们的代码虽然已经完成了打包,但是并没有被转义为低版本的代码。我们需要通过 Babel 来将 ECMAScript 2015+ 版本的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。

    安装 babel-loader

    npm i -D babel-loader
    

    此外我们还需要安装以下依赖

    npm i -D @babel/core @babel/preset-env @babel/plugin-transform-runtime
    npm i -S @babel/runtime @babel/runtime-corejs3
    

    webpack.config.js 配置 babel-loader

    // webpack.config.js 
    module.exports = {
      // ...
      module: {
        rules: [
          {
            test: /.jsx?$/,
            use: ['babel-loader'],
            exclude: /node_modules/ // 排除 node_modules 目录
          }
        ]
      }
      // ...
    }
    

    建议给 loader 指定 include 或是 exclude,排除一些不需要编译的目录可以提高编译效率,比如 node_modules 目录。

    有两种方式配置 babel

    1. 通过 .babelrc 文件配置
      根目录下新建一个 .babelrc 文件,配置如下:
    // .babelrc
    
    {
      "presets": ["@babel/preset-env"],
      "plugins": [
        [
          "@babel/plugin-transform-runtime",
          {
            "corejs": 3
          }
        ]
      ]
    }
    
    1. webpack 中配置 babel
    // webpack.config.js
    
    module.exports = {
      module: {
        rules: [
          {
            test: /.jsx?$/,
            use: {
              loader: 'babel-loader',
              options: {
                presets: ["@babel/preset-env"],
                plugins: [
                  [
                    "@babel/plugin-transform-runtime",
                    {
                      "corejs": 3
                    }
                  ]
                ]
              }
            },
            exclude: /node_modules/
          }
        ]
      }
    }
    

    通过执行 npm run dev, 我们查看 http://localhost:9000/bundle.js 发现已经是转义之后的低版本代码了。

    使用 source map

    webpack 打包源代码时,会很难追踪到错误和警告在源代码中的原始位置。为了更容易地追踪错误和警告,JavaScript 提供了 source map 功能,将编译后的代码映射回原始源代码。

    在开发环境中,可以设置 devtool 值为 inline-source-map,生产环境设置为 none 或者 source-map

    // webpack.config.js
    const isProduction = process.env.NODE_ENV == 'production';
    
    module.exports = {
      // ...
      devtool: isProduction ? 'source-map' : 'inline-source-map',
    }
    

    使用 source-map 最终会单独打包出一个 .map 文件,我们可以根据报错信息和 map 文件定位到源代码。

    但是一般不会直接将 .map 文件部署到 CDN,而是将 .map 文件传到错误监控系统,以便我们可以解析到出错的源码位置。

    处理样式文件

    webpack 只能处理 js 文件,如果要处理 css 需要借助 loader

    如果是 .css,我们需要的 loader 有: style-loadercss-loader,考虑到兼容性问题,还需要 postcss-loaderautoprefixer

    如果是 .less, 还需要 less-loaderless

    如果是 .sass 的话,还需要 sass-loadernode-sass

    安装相应的依赖

    npm i -D style-loader css-loader postcss-loader autoprefixer less-loader less sass-loader node-sass
    

    webpack.config.js 添加 csslesssass loader

    // webpack.config.js
    
    module.exports = {
      // ..
      module: {
        rules: [
          // ...
          {
            test: /.(c|le)ss$/,
            use: ['style-loader', 'css-loader', 'postcss-loader', 'less-loader'],
            exclude: /node_modules/ // 排除 node_modules 目录
          },
          {
            test: /.sass$/,
            use: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader'],
            exclude: /node_modules/ // 排除 node_modules 目录
          }
        ]
      }
    }
    

    根目录新建 postcss.config.js

    // postcss.config.js
    module.exports = {
      plugins: [
        // 兼容浏览器,添加前缀
        require('autoprefixer')({
          overrideBrowserslist: [
            "Android 4.1",
            "iOS 7.1",
            "Chrome > 31",
            "ff > 31",
            "ie >= 8"
            // 'last 10 versions', // 所有主流浏览器最近10版本用
          ],
          grid: true
        })
      ]
    }
    

    配置完成,新建几个文件测试一下

    /* src/index.css */
    div {
       200px;
      height: 100px;
      display: flex;
    }
    
    // src/index.less
    @color: yellow;
    body {
      background: @color;
      display: flex;
    }
    
    // src/index.sass
    $text-color: orange;
    div 
      color: $text-color;
      display: flex;
    

    再在入口文件中引入三个文件

    // src/index.css
    import './index.css';
    import './index.less';
    import './index.sass';
    

    我们可以看到样式生效并且 flex 也自动加上浏览器前缀了。

    需要注意的是 loader 的执行顺序是从右向左执行的,执行顺序为:

    less-loader/sass-loader => postcss-loader => css-loader => style-loader

    1. less-loader 处理编译 .less 文件,将其转为 css
    2. sass-loader 处理编译 .sass 文件,将其转为 css
    3. postcss-loaderautoprefixer,自动生成浏览器兼容性前缀
    4. css-loader 处理 css 中的 @importurl(...)等语句
    5. style-loader 动态创建 style 标签,将 css 插入到 head

    处理图片、字体等媒体文件

    我们可以使用 url-loader 或者 file-loader 来处理本地的资源文件。

    file-loader 就是将文件在进行一些处理后(主要是处理文件名和路径、解析文件 url),将文件移动到输出的目录中,同时在 require 文件的地方会返回文件的绝对路径。

    url-loader一般与 file-loader 搭配使用,功能与 file-loader 类似,如果文件小于限制的大小,则会返回 base64 编码。

    需要同时安装 file-loaderurl-loader

    npm i -D file-loader url-loader 
    

    配置 webpack.config.js

    // webpack.config.js
    
    module.exports = {
      // ..
      module: {
        rules: [
          // ...
          {
            test: /.(jpe?g|png|gif|webp|svg|eot|ttf|woff|woff2)$/i,
            use: [
              {
                loader: 'url-loader',
                options: {
                  limit: 10240, // 10K 资源大小小于 10K 时,将资源转换为 base64,超过 10K,将图片拷贝到 dist 目录
                  name: '[name]_[hash:6].[ext]', // 设置文件名,默认情况下,生成的文件的文件名就是文件内容的 MD5 哈希值并会保留所引用资源的原始扩展名
                  outputPath: 'assets', // 输出目录
                  esModule: false // 表示是否使用es6模块的导出,默认是启用的
                }
              }
            ],
            exclude: /node_modules/
          }
        ]
      }
    }
    

    我们修改 src/index.sass 文件

    // src/index.sass
    $text-color: orange;
    div 
      color: $text-color;
      display: flex;
      background: url('../images/author.jpg');
    

    修改了配置文件,我们重新 npm run dev 一下,可以看到图片地址已经被替换了

    npm run build 可以看到 dist/assets 有这个文件

    注意此时如果需要在 html 文件中引用这个图片需要这样写

    <body>
      <img src="<%= require('../images/author.jpg') %>">
    </body>
    

    最终打包之后的路径是

    打包前清空 dist 目录

    我们修改文件打包之后,生成的 hash 值和之前 dist 中的不一样,会导致 dist 下的文件越来越多,所以我们需要在打包前先清空 dist 目录。

    安装 clean-webpack-plugin

    npm i -D clean-webpack-plugin
    
    // webpack.config.js
    const { CleanWebpackPlugin } = require('clean-webpack-plugin');
    
    module.exports = {
      //...
      plugins: [
        // ...
        new CleanWebpackPlugin() 
      ]
    }
    
    

    这样每次打包前就会自动清除 dist 目录下的文件了。

    最后

    通过上面的实践,我们对 webpack 的基础配置有了一个初步的了解。本文所有代码可以查看github

    后续将会继续推出 webpack 系列的其他内容哦~

    喜欢本文的话点个赞吧~

    更多精彩内容,欢迎关注微信公众号~

  • 相关阅读:
    Exception in thread "main" java.lang.NoClassDefFoundError: com/google/common/base/Function问题解决
    Fiddler是最强大最好用的Web调试工具之一--网站抓包分析
    django 运行不同的settings
    Ununtu 15.04 安装MySql(Django连接Mysql)
    Linux SSH登录服务器报ECDSA host key "ip地址" for has changed and you have requested strict checking.错误
    解决将Ubuntu下导出的requirements.txt到Centos服务器上面出现pkg-resource的版本为0.0.0
    Ubuntu安装Nginx和正确卸载Nginx Nginx相关
    jquery 情况form表单的所有内容
    python把中文文档变为拼音
    将多个文件夹内的txt合并
  • 原文地址:https://www.cnblogs.com/alsy/p/12594946.html
Copyright © 2020-2023  润新知