zixinfeng

2018-07-05 22:58

webpack基础探讨

本文作者:IMWeb zixinfeng 原文出处:IMWeb社区 未经同意,禁止转载

1. 使用webpack

  • webpack命令使用

    • webpack --help (webpack -h) 查看所有的命令
    • webpack-v
    • 打包命令 webpack [] 不适用webpack配置文件的时候
    • 使用webpack配置文件, 直接webpack
      • --config 指定配置文件 (默认配置文件名称 webpack.config.js或者 webpackfile.js)
    • Basic Options
      • --entry 指定入口文件
      • --watch -w 检测文件系统的变化
      • --debug -d 打开调试状态
      • --devtool 生成sourceMap
      • --progress 进度条显示
      • -d 简写 --mode development
    • Module Options
    • Output Options
    • Advanced Options 高级选项
    • Resolved Options 解析选项
    • Optimization Options 优化选项
    • Stats Option 状态选项 (打包出来样式的选项)
      • 使用webpack配置(配合node npm使用)
    • 不同的配置文件, 开发环境, 生产环境, 测试环境, 打包给第三方使用的
      • 第三方的脚手架vue-cli
    • 交互式的初始化一个项目
    • 项目迁移v1->v2
      # wepbpack-cli的使用
      webpack-cli init webpack-addons-demo
      # 项目迁移
      webpack-cli migrate <config> ## 只会升级配置文件, package.json里面的文件 需要手机升级
      

      2. 直接使用webpack命名, 使用默认文件或者默认配置

   // app.js
   import sum from './sum'
   conosle.log(sum(1,2))

   // sum.js
   export default function sum(a, b) {
       return a + b;
   }

   //  打包命令: webpack app.js --output-path=dist --output-filename=bundle.js --mode development

   // 指定配置文件 webpack --config webpack.config.dev.js

3. 编译ES6/7

  • babel-loader

    ## 安装最新版本loader
    npm install babel-loader@8.0.0-beta @babel/core --save-dev
    ## 安装最新preset
    npm install @babel/preset-env --save-dev
    
  • npm install babel-loader babel-core --save-dev

  • npm install babel-preset-env --save-dev 指定规范的版本, 只是针对语法

    • es2015
    • es2016
    • es2017
    • env 包括2015~2017, 以及latest 用的比较多

      • 业内自定义的babel-preset-react
      • babel-preset-stage 0 ~3 表示规范组还没有正式发布阶段的
      • babel-presets - options - target 当需要编译的时候, 会根据指定的target来选择那些语法进行编译, 那些语法不进行编译
      • target.browsers 指定浏览器环境
      • target.browsers: 'last 2 versions' 主流浏览器的最后两个版本
      • target.browsers: '> 1%' 大于全球浏览器占有率1%的浏览器
      • 数据来源是 browserlist中, can i use中

        {
                test: /\.js$/,
                use: { // use: 'babel-loader'  //可以直接是一个字符串
                  loader: 'babel-loader',
                  options: {
                    // 指定preset
                    presets: [['env', {
                      // 告诉babel, 当需要编译的时候, 会根据指定的target来选择那些语法进行编译, 那些语法不进行编译
                      targets: {
                        browsers: ['> 1%', 'last 2 versions'],
                        // chrome: '52' // 一些新语法浏览器直接支持 不会被转换
                      }
                    }]]
                  }
                },
                exclude: '/node-modules/'
         }
        
         // 当同时指定'> 1%', 'last 2 versions'的时候, 箭头函数会被转化, const, let等被转化, set不会被转化, num**2 转成了Math.pow
         // 将targets换成  chrome: '52', 转化后代码基本和原生代码一样
        
    • target.node 指定node环境

  • babel-polyfill插件和babel-runtime-transform插件

    • 针对一些方法比如数组的map, includes, Set并没有被babel处理, 但是在一些低版本的浏览器中这些方法并没有被实现, 所以需要借助这两个插件
    • babel-preset 只是针对语法, 而这两个插件针对函数和方法
      • generator
      • Set
      • Map
      • Array.from
      • Array.prototype.includes
    • 上述方法都没有被babel处理, 所以就需要借助babel的插件进行处理
  • babel-polyfill 垫片, 浏览器之间标准实现的方式不一样,保持浏览器之间同样的API
    • 全局垫片 (只要引入, 在全局范围内整个浏览器范围内, 可以对实现的API进行调用)
    • 相当于对全局变量的一个污染, 为开发应用而准备的 (在业务中使用, 而不是框架比如vue)
    • 使用: npm install babel-polyfill --save 真实项目中的依赖 所以是--save
    • 在项目中使用 import 'babel-polyfill'
  • babel-runtime-transform
    • 局部垫片
    • 为开发框架而准备的, 不会污染全局变量, 会在局部的方法里面新增加变量和方法
    • 优势: 当在代码中使用它的时候, 项目中的其他函数,如果使用es6/7方法, 会将每一个引用到的方法打包到单独的文件中去的; 如果使用了runtime-transform, 将其作为一个独立的整体单独打包进去, 相当于文件之间多余的代码就不会再有了
    • npm install babel-plugin-transform-runtime --save-dev
    • npm install babel-runtime --save
  • .babelrc 在里面配置和babel插件相关的内容

    // app.js
    import sum from './sum'
    
    const func = () => {
      console.log('hello babel')
    }
    
    func()
    
    const arr = [1, 2, 3, 4, 5, 4, 3, 2, 1]
    const arrb = arr.map(item => item * 2)
    
    // 下面的语句 不会经过runtime编译
    arr.includes(5);
    
    // 会经过runtime编译  但是没有exports 使用的时候报错
    console.log('SetB', new Set(arrb))
    
    /*
    function* gen() {
      yield 1
    }
    */
    sum(1, 2)
    
    // .babelrc
    {
      "presets": [["env", {
        "targets": {
          "browsers": ["> 1%", "last 2 versions"]
        }
      }]],
      "plugins": [
        "transform-runtime"
      ]
    }
    
    // 1. 当plugins为空的时候, 上面的代码会完整运行, 都不会被转义
    // 2. 添加generator函数的时候, 会报错找不到 regenerator
    // 3. 添插件的时候 includes不会编译, Set, generator会编译, 但是报错$export is not a function
    // 4. 屏蔽插件plugins, 使用polyfill, 完美运行所有新属性, 但是打包文件很大, 达到了471Kb
    
  • 实际开发中如何选择

    • 如果是应用开发, 只需要配置preset, 如果要使用es6/7新语法, 使用polyfill
    • 如果是开发UI库, 框架, 使用runtime

4. 编译TypeScript

  • JS的超集 tslang.cn 来自于微软
  • 官方推荐: npm install typescript ts-loader --save-dev
  • 第三方loader: npm install typescript awesome-typescript-loader --save-dev
  • 配置: tsconfig.json
    ## 常用配置选项
    compilerOptions:告诉编译器常用的配置选项, 比如 允许js 模块化方式指定:commonjs 指定输出路径等
    compilerOptions.module: 模块化方式指定
    compilerOptions.target: 编译之后的文件在什么环境下运行的 (类似将语言编译到什么程度)
    compilerOptions.typeRoots: [
      "./node_modules/@type", // 默认安装npm install @types/lodash时路径
      "./typings/modules",    // 使用typings安装的声明文件路径
    ] 指定types声明文件所在的地址
    include: 给出一系列的文件路径, 表示需要编译的文件
    exclude: 忽略的文件
    allowJs: 是否允许js的语法
    
  • 安装声明文件.这样在编译的时候就会给出警告错误, 告诉我们传递的参数类型有错误
    • npm install @types/lodash
    • npm install @types/vue
    • 或者使用typings安装types声明文件, 使用compilerOptions.typeRoots

5. 提取公用代码

  • 减少冗余代码(每一个页面都会存在公共代码, 造成带宽浪费)
  • 提高用户的加载速度(只加载页面所需要的依赖, 其他页面在加载的时候, 公共代码已经加载好了)
  • CommonChunkPlugin (webpack.optimize.CommonChunkPlugin) // 针对webpack3 {

    plugins: [
      new webpack.optimize.CommonChunkPlugin({
        name: String | Array, // 表示chunk的名称 ?
        filename: String, // 公用代码打包的文件名称
        minChunks: Number|function|Infinity // 数字表示为需要提取的公用代码出现的次数(最小是多少, 比如出现两次以上就提取到公用代码), Infinity 表示不讲任何的模块打包进去, 函数的话表示自定义逻辑
        chunks: 表示指定提取代码的范围, 需要在哪几个代码快中提取公用代码
        children: 是不是在entry的子模块中 还是在所有模块中查找依赖
        deepChildren
        async: 创建一个异步的公共代码流
      })
     ]
    }
    

    // webpack4 optimization: {

      splitChunks: {
          chunks: 'all', // async(默认, 只会提取异步加载模块的公共代码), initial(提取初始入口模块的公共代码), all(同时提取前两者),
          minSize: 0, // 30000, // 大于30K会被抽离到公共模块
          minChunks: 2, //  模块出现一次就会被抽离到公共模块中, 如果是1的话, 表示将所有的模块都提走, 针对pageA中, 如果只有自己引用jQuery, 那么会生成jQuery-vendor.js 的打包文件
          maxAsyncRequests: 5, // 异步模块, 一次最多只能加载5个,
          maxInitialRequests: 3, // 入口模块最多只能加载3个
          name: true
      }
    }
    
  • 场景
    • 单页应用
    • 单页应用 + 第三方依赖
    • 多页应用 + 第三方依赖 + webpack生成代码 (webpack内置函数)
  • 针对单入口的commonChunksPlugin = 并没有将公共部分打包, 只有针对多入口才会
  • 多入口文件的时候

    entry: {
      pageA: path.resolve(__dirname, 'src/cmp', 'pageA'),
      pageB: path.resolve(__dirname, 'src/cmp', 'pageB')
      // vendor: ['lodash']
    },
    
    // webpack3
    plugins: [
        new webpack.optimize.CommonPluginsChun({
            name: 'vendor',
            minChunks: Infinity
        })
        // 公共模块打包的名字为vendor, entry中也有vendor, 所以会将webpack生成代码以及lodash打包进vendor中
    ]
    
    // webpack4
    splitChunks: {
          chunks: 'all', // async(默认, 只会提取异步加载模块的公共代码), initial(提取初始入口模块的公共代码), all(同时提取前两者),
          minSize: 30000, // 大于30K会被抽离到公共模块
          // minChunks: 2, //  模块出现两次次就会被抽离到公共模块中
          minChunks: Infinity, // 不需要在任何的地方重复
          maxAsyncRequests: 5, // 异步模块, 一次最多只能加载5个,
          maxInitialRequests: 3, // 入口模块最多只能加载3个
          // name: 'common' // 打包出来公共模块的名称
          name: 'vendor' // 打包出来公共模块的名称
    }
    
    // 1. 会将pageA, pageB中 公共使用的模块打包成进common.chunk.js (name:'common'的时候), 公共模块中包括webpack生成的代码 
    
    // 2. lodash只在pageA中使用, 次数为1, 但是minChunks: 2, 所以lodash只会被打包进pageA中
    
    // 3. 在entry中添加 vendor: ['lodash'] 将公共库lodash单独打包, 在webpack4中将其打包进了公共common.chunk中, vendor中只有对lodash的引用
    
    // 4. 如果想将lodash和webpack运行生成时代码以及公共代码打包到一起, minChunks改成Infinity, name:vendor,  将所有生成的文件引用都放到vendor中了
    
    // 5. 保持第三方代码的纯净, 即将第三方代码和webpack分离开, webapck3添加plugins, webpack4添加runtimeChunk配置
        // webpack3
        new webpack.optimize.CommonPluginsChun({
            name: 'manifest',
            minChunks: Infinity
        })
        // 发现vendor和manifest处大部分代码是一样的可以, 可以改成
        new webpack.optimize.CommonPluginsChun({
            names: ['vendor','manifest'],
            minChunks: Infinity
        })
    
        // webpack4
        runtimeChunk: {
          name: 'manifest'
        },
    
        // 结果是: 将webpack生成的代码打包到manifest中, 将lodash打包进vendor中, 将引用次数超过两次的打包进vendor中
    

6. 代码分割和懒加载

  • 通过代码分割和懒加载, 让用户在尽可能的下载时间内加载想要的页面, 只看一个页面的时候, 下载所有的代码, 带宽浪费;
  • 在webpack中, 代码分割和懒加载是一个概念, webpack会自动分割代码, 然后再把需要的代码加载进来, 不是通过配置来实现的, 通过改变写代码的方式来实现的, 当依赖一个模块的时候, 告诉webpack我们是需要懒加载或者代码切分, 通过两种方式来实现

    • webpack.methods
      • require.ensure() 接收四个参数
      • 第一个参数dependencies, 加载进来的代码并不会执行, 在callback中引入, 这个时候才会去执行, 第三个参数errorBack, 第四个参数chunkName
      • 如果浏览器不支持promise, 需要添加垫片
      • require.include 只有一个参数, 只引入进来, 但不执行
        • 当两个子模块都引入了第三个模块, 可以将第三个模块放入父模块中, 这样动态加载子模块的时候, 父模块已经有了第三方模块, 不会在多余加载; 比如subPageA, subPageB都引入了moduleA, 但是moduleA不会被打包进父依赖, 所以可以使用include
    • ES2015 loader spec (动态import) stage-3
      • 早起system.import
      • 后来import方式 返回一个Promise
        • import().then
    • webpack import function 通过注释的方式来解决动态的chunkName以及加载模式
      import(
          /*webpackChunkName: async-chunk-name*/
          /*webpackMode: lazy*/
          moduleName
      )
      
  • 代码分割的场景

    • 分离业务代码和第三方依赖 (提取公共代码中有涉及)
    • 分离业务代码 和 业务公共代码 和 第三方依赖; 相比于上一个,将业务代码拆成两部分
    • 分离首次加载 和 访问后加载的代码 (访问速度优化相关的) - LazyLoad - 提高首屏加载速度
   // 0. 单入口pageA, 不做任何的优化 直接引入 subPageA, subPageB, lodash 会发现pageA非常大

   // 1. 异步引入, 将lodash打包到vendor中
   require.ensure('lodash', require => {
     const _ = require('lodash')
     _.join([1, 2, 3], 4)
     console.log(_)
   }, 'vendor')

   // 2. pageA.js中修改
   if (page === 'subPageA') {
     // require([]) 参数是空数组的话, 里面的require的包还是会被异步打包
     require.ensure(['./subPageA'], require => {
       // 如果不require的话, 那么就不会执行subPageA中的代码块
       const subPageA = require('./subPageA')
       console.log(subPageA)
     }, 'subPageA')
   } else if (page === 'subPageB') {
     require.ensure(['./subPageB'], require => {
       const subPageB = require('./subPageB')
       console.log(subPageB)
     }, 'subPageB')
   }
   // 结果: moduleA分别在打包好的文件 subPageA.chunk.js 和 subPageB.chunk.js中, 公共部分moduleA没有被提取出来

   // 3. 单entry有上述公共代码的情况的话, 使用inlcude的情况处理, 将module在父模块pageA.js提前引入, 但是并不运行
   require.include('./moduleA')
   // 结果: moduleA被打包进入了pageA.bundle.js中, 这样就完成了代码分割


   // --- import 方案 ------------- 
   /*   坑: import 只有在stage-0 或者 syntax-dynamic-import
       yarn add babel-preset-stage-0 babel-plugin-syntax-dynamic-import --dev
       .babelrc   { "presets": ["stage-0"], "plugins": ["syntax-dynamic-import"] }
       上述两种情况只使用一种即可
   */
   // 在import的时候 代码实际上已经执行了
   if (page) {
     import(
       /* webpackChunkName: "subPageA" */
       /* webpackMode: "lazy" */
       './subPageC'
     ).then(subPageC => {
       console.log(subPageC)
     })
   } else {
     import(
       /* webpackChunkName: 'subPageD' */
       /* webpackMode: "lazy" */
       './subPageD'
     )
   }
  • async 在代码分割中如何使用, 即结合commonChunkPlugin

    // webpack.plugin.lazy.cmp.js
    entry: {
        pageA: path.resolve(__dirname, 'src/lazy_cmp', 'pageA'),
        pageB: path.resolve(__dirname, 'src/lazy', 'pageB'),
        vendor: ['lodash']
    }
    
    // webpack3
    plugins: [
        new wepback.optimize.CommonsChunkPlugin({
               // async 指定为true表示异步模块, 或者指定为 异步模块提取后的名称
            async: 'async-common',
            children: true, // 表示不仅仅是两个入口页面之间, 而且还是两个页面之间的子依赖中去寻找
            minChunks: 2
        }),
        new wepback.optimize.CommonsChunkPlugin({
            // lodash打包进入vendor中, manifest是webpack运行时代码
            names: ['vendor', 'manifest'],
            minChunks: Infinity
        })
    ]
    
    // webpack4
    optimization: {
        // webpack runtime 代码
        runtimeChunk: {
          name: 'manifest'
        },
        // 公共模块提取
        splitChunks: {
          chunks: 'all', // async(默认, 只会提取异步加载模块的公共代码), initial(提取初始入口模块的公共代码), all(同时提取前两者),
          minSize: 30000, // 大于30K会被抽离到公共模块
          // minChunks: 2, //  模块出现两次次就会被抽离到公共模块中
          minChunks: Infinity, // 不需要在任何的地方重复
          maxAsyncRequests: 5, // 异步模块, 一次最多只能加载5个,
          maxInitialRequests: 3, // 入口模块最多只能加载3个
          name: 'vendor' // 打包出来公共模块的名称
        }
    }
    
    // pageA.js
    import _ from 'lodash'
    
    / 1. 这里不再使用include, 因为会和pageA打包到一起, 这里的目的是 将其异步单独提取出来
    // require.include('./moduleA')
    
    const page = 'subPageA' // 在pageB中, 这里page='subPageB', 其余一样
    if (page) {
      import(
        /* webpackChunkName: "subPageA" */
        /* webpackMode: "lazy" */
        './subPageA'
      ).then(subPageA => {
        console.log(subPageA)
      })
    } else {
      import(
        /* webpackChunkName: 'subPageB' */
        /* webpackMode: "lazy" */
        './subPageB'
      )
    }
    
    // 2.  webpack3 结果:  将异步打包结果中subPageA和subPageB中的公共模块moduleA, 单独的提取到了async-common-pageA.chunk.js中
        这里比较坑的困惑: commonsChunkPlugin参数说的不是很明确, 比如async, children, deepChildren, minChunk, 他们之间是有依赖忽视关系的
    
    // 3. webpack4 结果: chunks:all, 结果是将多次引用的公共模块moduleA, lodash提取到了vendor.chunk中, 其余的和webpack3一样, 生成打包文件pageA.chunk, pageB.chunk(入口文件), subPageA.chunk, subPageB.chunk(异步单独提取), manifest.chunk(webpack-runtime单独提取)
    

5. 处理CSS

  • 每一个模块都有自己的css文件, 在使用的时候将css样式引入
  • 如何在webpack中引入css

    • style-loader 在页面中创建style标签, 标签里面的内容就是css内容
      • style-loader/url
      • style-loader/useable
    • css-loader 如何让js可以import一个css文件, 包装一层, 让js可以引入css文件

      // index.js
      import './css/base.css'
      
      // webpack.config.style.js
          {
              test: /\.css$/,
              use: [
                {
                  loader: 'style-loader'
                },
                {
                  loader: 'css-loader'
                }
              ]
           }
      
      // 将打包后的文件引入到index.html中
      // 1. 结果: 在html中生成了style标签, 将base.css标签中的样式放到了style标签中
      
      // 2. 生成link标签的形式 (不过用的比较少) 注意publicPath配置
      use: [
                {
                  loader: 'style-loader/url'
                  // loader: 'style-loader/useable'
                },
                {
                  loader: 'file-loader'
                }
           ]
      // 结果: style-loader/url 单独生成一份css文件 , 但是引入多个文件的时候, 会生成多个link标签, 会造成越多的网路请求
      
      //3. style-loader/useable
      import base from 'base.css'
      import common from 'common.css'
      var flag = false;
      setInterval(function() {
          if(flag) {
              base.use()
          } else {
              base.ununse()
          }
          flag = !flag;
      }, 2000)
      // base.use() 样式插入到style标签中
      // common.unuse() // 控制样式不被引用
      // 结果: 没过2000ms, 页面中样式循环引用和删除
      

6. StyleLoader 配置

  • insertAt (插入位置)
  • insertInto(插入到DOM)
  • singleton (是否只使用一个style标签) 当css模块比较多的时候 会有很多css标签
  • transform (转化, 浏览器环境下, 插入页面之前)
  •        transform: './src/style/css.transform.js'
    
           // css.transform.js 文件内容
    
           // 该函数并不是在打包的时候执行的,在运行webpack的时候, 是不行执行的
           // 在style-loader 将样式插入到DOM中的时候 执行的, 运行的环境是在浏览器环境下, 可以拿到浏览器参数, window,UA
    
           // 可以根据当前浏览器来对当前的css进行形变
           module.exports = function(css) {
             console.log(css)
             console.log(window.innerWidth)
             // 输出形变以后的css
             if (window.innerWidth >= 768) {
               css = css.replace('yellow', 'dodgerblue')
             } else {
               css = css.replace('yellow', 'orange')
             }
             return css;
           }
       - 针对每一次在index.js中引入的css文件都会执行上面的代码
    
    • CssLoader 配置参数

      • alias 解析的别名 将引入css的路径映射到其他地方
      • importLoader 取决于css处理后面是不是还有其他的loader (sass会使用到 @import)
      • minimize 是否压缩
      • modules 是否启用css-modules
      • 打包出来的样式class 都变成一段随机字符串
        • CSS modules
        • :local 给定一个本地的样式 局部的样式
        • :global 给定一个全局样式
        • compose 继承一个样式
        • compose ... from path 引入一个样式 (尽量将composes放在前面, 这样可以控制引入顺序, 样式不会被覆盖) // base.css .box { composes: big-box from './common.css'; height: 200px; width: 100px; border-radius: 4px; background: #696969; }
    • localIdentName: '[[path]][name]_[local]--[hash:base64:5]' 控制生成的class类名

      • path代表引用css路径 name表示文件名称 local本地样式名称
        • 配置less/sass
    • npm install less-loader less --save-dev
    • npm install sass-loader node-sass --save-dev
      .header {
        composes: font from './header.less'
      }
      
      • 提取css代码 - 提取公共代码 做缓存 (不提取的话, 将css代码打包到了js文件中)
      • extract-loader
      • ExtractTextWebpackPlugin
      • npm install extract-text-webpack-plugin --save-dev
       // webpack3
       var ExtractTextWebpackPlugin = require('ExtractTextWebpackPlugin)

       module: {
           rules: [

               {
                   test: /\.less$/,
                   use: ExtractTextWebpackPlugin.extract({
                       fallback: {
                           // 告诉webpack, 当不提取的时候, 使用何种方式将其加载到页面中
                           loader: 'style-loader,
                           options: {
                               singleton: true,
                               // transform: ''
                           }
                       },
                       use: [
                           {loader: 'css-loader'}
                           {loader: 'less-loader'}
                       ], // 定义我们继续处理的loader
                   })
               }
           ]
       },
       plugins: [
           new ExtractTextWebpackPlugin({
               filename: '[name].min.css', // 提取出来的css的名称
               // 将css-loader的option中的minimize打开

               // allChunks 给插件指定一个范围, 指定提取css的范围
               // 1. 设置为true 表示所有的引用的css文件都提取
               // 2. 设置为false, 默认, 只会提取初始化的css(异步加载不认为是初始化)
               allChunks:false, 
           })
       ]
       // webpack3 结果: index.bundle.js app.min.css 但是打开index.html 并没有插入进去

       // webpack4 
       {
           test: /\.less$/,
           use: [
             MiniCssExtractPlugin.loader,
             {
               loader: 'css-loader',
               // loader: 'file-loader'
               options: {
                 minimize: process.env.NODE_ENV === 'production',
                 modules: true,
                 localIdentName: '[path]_[name]_[local]--[hash:base64:5]'
               }
             },
             {
               loader: 'less-loader'
             }
           ]
       }

        plugins: [
           new MiniCssExtractPlugin({
             // Options similar to the same options in webpackOptions.output
             // both options are optional
             filename: '[name].css',
             chunkFilename: '[id].css'
           })
       ]
 - 异步引入a.js文件, 在a.js文件中引入a.less
   1. 针对allChunks为false的情况
      - webpack3: 生成a.bundle.js文件, css文件被当成js的一个模块被打包处理, 将css放在js文件里面, 一起被提取; css代码切分的一种方式, 将初始化加载和动态加载区分开; 借助动态加载的代码区分, 也是css-in-js的一个概念
      - weboack4: 生成moduleA.chunk.js 和moduleA.chunk.css文件, 在index.bundle.js 包括了对于modulA.js和module.css文件的引用
   2. webpack4使用splitChunks配置
          optimization: {
            splitChunks: {
              cacheGroups: {
                styles: {            
                  name: 'styles',
                  test: /\.scss|css$/,
                  chunks: 'all',    // merge all the css chunk to one file
                  enforce: true
                }
              }
            }
          }
      - 结果: 生成index.bundle.js style.chunk.js style.chunk.css 将所有的样式文件都打包进了style.chunk.css文件中, 但是需要手动添加到项目htm中
      - question: 为什么这里不会运行? npm run extract
  • PostCss (Autoprefixer CSS-nano CSS-next) A tool for transforming Css With Javascript用js去转化css的一个工具
    • 联系到上一节中的css.transform.js, 但是时机是不一样的, PostCss是打包的时期, css.transform是浏览器插入到style标签中的时候
    • postcss的强大, 理解成为一个处理css的工具
      • 安装 npm install postcss postcss-loader autoprefixer cssnano postcss-cssnext --save-dev
      • autoprefixer: 帮助加上浏览器前缀
      • css-nano 帮助我们优化压缩css, 在postcss可以当做插件使用, css-loader就是用的css-nano做的压缩
      • css-next 使用未来的css新语法
      • css variables
      • custom selectors 自定义选择器
      • calc() 动态计算 ...
         {
             loader: 'postcss-loader',
              options: {
                 // require进来的插件给postcss使用的
                 ident: 'postcss', // 表明接下来的插件是给postcss使用的
                 plugins: [
                     // require('autoprefixer')(),
                     // 两个一起用cssnext 会给出警告, 提示已经包含autoprefixer
                     require('postcss-cssnext')()
                 ]
             }
           },
  • 一旦涉及到浏览器兼容性问题的时候, 一定会有针对的浏览器兼容问题, 使用browserlist, 让所有的插件都公用一份browserlist
    • 可以放在package.json里面
    • .browserlistrc 存入对浏览器的要求
      • postcss-import 插件 将@import的文件内容直接放入到当前的css文件中, 但是存过来之后要考虑相对文件路径的变化, 需要配合postcss-url来使用
0条评论

    您需要 注册 一个IMWeb账号或者 才能进行评论。