webpack的世界

本文也是多次学习webpack积累下来的知识点,一直在云笔记里。

webpack的原理

webpack构建流程
从启动webpack构建到输出结果经历了一系列过程,它们是:

  • 解析webpack配置参数,合并从shell传入和webpack.config.js文件里配置的参数,生产最后的配置结果。
  • 注册所有配置的插件,好让插件监听webpack构建生命周期的事件节点,以做出对应的反应。
  • 从配置的entry入口文件开始解析文件构建AST语法树,找出每个文件所依赖的文件,递归下去。
  • 在解析文件递归的过程中根据文件类型和loader配置找出合适的loader用来对文件进行转换。
  • 递归完后得到每个文件的最终结果,根据entry配置生成代码块chunk。
  • 输出所有chunk到文件系统。

需要注意的是,在构建生命周期中有一系列插件在合适的时机做了合适的事情,比如UglifyJsPlugin会在loader转换递归完后对结果再使用UglifyJs压缩覆盖之前的结果

配置webpack就是在配置一个node的模块module.exports用于webpack来读取

1、webpack与Rollup的优缺点和区别?

webpack的优点:

  • 专注于处理模块化的项目,能做到开箱及用、一步到位
  • 可用plugin扩展,完整好用又不失灵活
  • 使用场景不限于web开发
  • 社区庞大

webapck的缺点:

  • 只能用于采用模块化开发的项目

Rollup

  • 可以对es6进行 Tree Shaking
  • 它支持导出ES模块的包
  • 它支持程序流分析,能更加正确的判断项目本身的代码是否有副作用

2、devserver的使用

webpack-dev-server是一个启动服务的程序,可以自动去,读取webpack.config.js 的文件。执行webpack-dev-server来启动,就可以来自动刷新页面,内部的原理是express的启服务,用webSocket来通知浏览器。

3、webapck配置选项

  • Entry:入口,webpack执行构建的第一步从entry开始
  • Module:模块,一切都是模块,一个模块对应一个文件。webapck从entry开始递归找出依赖的模块。
  • Chunk:代码块,一个chunk由多个模块组合而成,用于代码合并与分割。
  • Loader:模块转换器,将模块的原内容按照要求转换成新的内容。
  • Plugin: 扩展插件,在特定时机执行
  • Output:输出结果,把处理的最终想要的代码输出结果

4、Entry的配置

1、context是webpack寻找相对路径文件会以context为根目录
module.exports = {
    context: path.resolve(__dirname, './app'),
    entry:'./main.js',
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, './dist')
    }
}
2、Entry
  • string 'app/'
  • array ['app/a.js', 'app/b.js'] 多入口,单Chunk
  • object [a:'a.js', b:'b.js'] 配置多个入口,每个入口生成一个Chunk

也可以动态的配置,就是返回函数,可以同步也可以异步

同步
entry:()=>{
        return {
            a: './pages/a',
            b:'./pages/a'
        }
    },
异步
    entry:new Promise((resolve)=>{
        resolve({
            a: '/a.js',
            b:'/b.js'
        })
    }),

5、Output配置

output是一个对象,里面有配置项

  1. filename 输出文件的名称,是string
  • [id Chunk的唯一标识,从0开始]
  • [name Chunk的名称]
  • [hash] Chunk的唯一标识Hash[hash:8]可以指定长度
  • [chunkhash] Chunk内容的Hash值,比如 ExtractTextWebapckPlugin就是用的那个
  1. chunkFilename 配置无入口的Chunk的输出名字,比如CommonChunkPlugin import('path/')动态加载
  2. path输出文件存在的本地目录:path: path.resolve(__dirname, './dist')
  3. publicPath 配置上传cnd的路径
  4. corssOriginLoading 在异步加载是,利用JONSP的原理动态插入script,用来控制crossorigin的值
    当本地尝试使用 window.onerror 去记录脚本的错误时,跨域脚本的错误只会返回 Script error。
    HTML5 新的规定,是可以允许本地获取到跨域脚本的错误信息,但有两个条件:
  • 一是跨域脚本的服务器必须通过 Access-Controll-Allow-Origin 头信息允许当前域名可以获取错误信息
  • 二是当前域名的 script 标签也必须指明 src 属性指定的地址是支持跨域的地址,也就是 crossorigin 属性

可以取一下值:

  • anonymous(默认) 匿名的,加载不会带上用户的cookies
  • use-credentials,在加载脚本是会带上用户的cokires
  1. libraryTarget 和 library
  2. 构建一个可以被导入使用的库
  • libraryTarget 何种方式,var(默认)comomjs/commonjs2/this/window/global
  • library 导出的名字

6、module

  • rules来配置Loader
rules: [{
    test: '/.js$/',//命中文件
    use: ['babel-loader?cacheDirectory'],//指定loader  解析是从后向前
    include: path.resolve(__dirname, 'src'),//包括 
    exclude: path.resolve(__dirname, 'node_modules')//排除
  },{
    test: /.vue$/,
    use: ['vue-loader']
  }]
  
  
  vue需要使用vue-loader
  • noParse 来忽略没有采用模块化的文件的递归

        noParse: /jquery|chartjs/ //一般是正则 被忽略的文件不应该有import/requie/define等
  • parse可以细颗粒度配置解析比如AMD,commonJS等

7、resolve 配置webpack如何寻找模块

resolve: {
    alias: {
        components : './src/components'
    },
    mainFields: ['jsnext:main', 'browser', 'main'],//jsnext 是支持ES6的模式进入   
    extension: ['.ts','.js','.json'],//后缀列表 //延长;延期
    modules: ['./src/components', 'node_modules'],//指定本地解析 import 'button'
    enforceExtension: true,//开启必须带后缀  
},

8、plugin 接受数组,plugin的实例

9、devServer 使用devserver启动还生效

devServer:{
    hot: true,//启用 webpack 的模块热替换特性
    inline: true,//fasle是 iframe 模式
    historyApiFallback: true,//spa history的模式,
        任何请求都会返回index.html 也可以使用重写rewrites来详细配置
    contentBase:[path.join(__dirname, "public"),
        path.join(__dirname, "assets")],//提供静态文件 string fales关闭 array
    host:'0.0.0.0',//服务器外部可访问
    port: '9090',//端口
    disableHostCheck: true,//关闭host,devServer默认是接受本地请求配合host使用
    https:true,//开启https或者自己导入证书
    compress: true,//默认false开启gzip压缩   压缩
    open: true,//打开浏览器
    proxy:{ //   credentials 证书  设置成include,表示允许跨越传递cookie
        "/api": "http://localhost:3000"
    }
},

10、其他配置项

target:'web',//构建到的环境比如node web webworker等
    devtool:'source-map',//  可以设置为false
    watch:true,//监听文件,默认是关闭的devserer默认打开
    externals:{
        jquery: 'jQuery'
    },
    resolveLoader:{ //加载本地loader
},

11、为单页应用生成html

使用 html-webpack-plugin

12、其他常用Loader

file-loader 将js和css中的图片等替换成正确的地址,输出在文件中
url-loader 可以将文件的内容经过base64编码后注入js或者css中,但是要限制大小

{
        exclude: [
          /.(js|jsx)(?.*)?$/,
          /.(css|scss)$/,
          /.json$/,
          /.bmp$/,
          /.jpe?g$/,
          /.png$/,
        ],
        loader: require.resolve('file-loader'),
        options: {
          name: 'static/media/[name].[hash:8].[ext]',
        },
      },
      {
        test: [/.bmp$/, /.jpe?g$/, /.png$/],
        loader: require.resolve('url-loader'),
        options: {
          limit: 10000,
          name: 'static/media/[name].[hash:8].[ext]',
        },
      },

raw-loader 和 svg-inline-loader 可以把svg内嵌到网页,把svg当图片用,可以用上面的。

13、Source Map

以方便在浏览器通过代码调试

devtool有很多的取值,由一下6个关键字随意组合而成

  • eval: 用eval语句包裹需要安装的模块

  • source-map:生成独立的Source Map 文件

  • hidden:不在javascript文件中支出source map 文件的所在,这样浏览器就不会自动加载source map

  • inline:将生成的source map转成base64格式内嵌在javascript文件中

  • cheap:在生成的source map中不会包含列信息,那样计算量更小,输出文件更小,同时loader输出的source map不会被采用

  • module:来时loader的source map 被简单处理成每行一个模块

  • eval: 生成代码 每个模块都被eval执行,并且存在@sourceURL

  • cheap-eval-source-map: 转换代码(行内) 每个模块被eval执行,并且sourcemap作为eval的一个dataurl

  • cheap-module-eval-source-map: 原始代码(只有行内) 同样道理,但是更高的质量和更低的性能

  • eval-source-map: 原始代码 同样道理,但是最高的质量和最低的性能

  • cheap-source-map: 转换代码(行内) 生成的sourcemap没有列映射,从loaders生成的sourcemap没有被使用

  • cheap-module-source-map: 原始代码(只有行内) 与上面一样除了每行特点的从loader中进行映射

  • source-map: 原始代码 最好的sourcemap质量有完整的结果,但是会很慢

eval和.map文件都是sourcemap实现的不同方式,虽然大部分sourcemap的实现是通过产生.map文件, 但并不表示只能通过.map文件实现。下面是eval模式后产生的模块代码

cheap关键字的配置中只有行内,列信息指的是代码的不包含原始代码的列信息

  • 在开发环境 用source-map 那个最完整 或者cheap-module-eval-source-map 那个最快
  • 在生产环境就flase 或者 hidden-source-map 为错误搜集用
  • 如果需要加载模块的source-map 需要使用source-map-loader
  • vue-cli 用的devtool: '#eval-source-map',

14、代码检测和npm配合

与npm 配着
npm 可以运行node__modules里安装的程序
可以缩短命令
代码检测

javascript 用eslint 用.eslintrc json 文件配置 eslint-loader enforce: 'pre'
ts 用TSlint tslint.json 配置 tslint-loader
css stylelint 用.stylelintrc 配置 StyleLintPlugin 插件来处理

代码检测会变慢构建速度,可以配置IDE来检测,同时配置git Hook在代码提交时检测

15、node运行webapck和中间件

var config = require('../config')
var compiler = webpack(webpackConfig)

调用compiler的watch可以监听文件变化

require('webpack-dev-middleware')是express的一个插件

var app = express()
var compiler = webpack(webpackConfig)

var devMiddleware = require('webpack-dev-middleware')(compiler, {
  publicPath: webpackConfig.output.publicPath,
  stats: {
    colors: true,
    chunks: false
  }
})

但是不支持hot模式需要require('webpack-hot-middleware')

app.use(hotMiddleware)

代理使用

var proxyMiddleware = require('http-proxy-middleware')

16、webapck构建优化

1、减小文件的搜索范围
  1. 优化loader配置,利用好include和exclude
  2. 优化resolve.modules 使用绝对路径
modules: [path.resolve(__dirname, 'node_modules')]
  1. alias 指向 x.min.js 文件,但是lodash不适合
alias: {
        react: path.resolve(__dirname, './node_modules/react/dist/react.min.js')
    },
  1. noParse 配置不解析
noParse: /jquery|chartjs/ //一般是正则 被忽略的文件不应该有import/require/define等
2、优化构建过程
  1. babel-loader 加 cacheDirectory
  2. DllPlugin 官方推荐做法是把不常变动的文件打DLL

工程就把 react react-dom prop-types classnames mobx mobx-react lodash moment polyfill 等打进来 可以使用 [npm version]_dll.js 用 npm version 的话只要 version 一改变我们会重新打包,比如升级了 react ,我们就会 version +,就会重新打包。

先创建一个webpack.config.dll.js

执行webpack --config ./webpack.config.dll.js把需要dll的文件输出到dist/dll

const webpack = require('webpack');
const path = require('path');

const {version} = require('./package.json');

module.exports = {
    entry: {
        'react': [
            'react', 'react-dom',
            'prop-types',
            'classnames',
            'lodash', 'moment'
        ]
    },
    output: {
        path: path.join(__dirname, 'dist/dll'),
        filename: `[name].${version}.js`,
        library: 'dll_[name]',
        publicPath: '/dist/dll/'
    },
    plugins: [
        new webpack.DllPlugin({
            path: path.join(__dirname, 'dist/dll/', '[name].manifest.json'),
            name: 'dll_[name]'
        })
    ]
};


在webapck配置文件中使用

new webpack.DllReferencePlugin({
            context: __dirname,
            // 在这里引入 manifest 文件
            manifest: require('./dist/dll/react.manifest.json')
        }),
//把js插入到html文件中
new AddAssetHtmlPlugin({
    filepath: require.resolve(`./dist/dll/${getDLLFileName()}`),
    outputPath: 'dll',
    includeSourcemap: false,
    hash: true,
    publicPath: '/dist/dll/'
})

原理就是提前构建,缓存起来,用window的全局变量来取。

  1. happypack 对 build 的速度大大大提示,可以多线程打包,cache 也让 rebuild 加快
{
    test: /.(js|jsx)$/,//命中文件
    use: 'happypack/loader?id=js',//指定loader
    include: path.resolve(__dirname, 'src'),//包括
    exclude: path.resolve(__dirname, 'node_modules')//排除
   }, {
    test: /.(css|scss)$/,
    loader: 'happypack/loader?id=css'
}
new HappyPack({
    id: 'js',
    threadPool: happyThreadPool,
    loaders: [{
        path: 'babel-loader',
        query: {
            cacheDirectory: true
        }
    }]
}),
new HappyPack({
    id: 'css',
    threadPool: happyThreadPool,
    loaders: ['style-loader','css-loader', 'sass-loader']
}),

原理:happypack 的原理是让loader可以多进程去处理文件,css和js,图片和文件支持不好

  1. Devtool 使用 开发用cheap-module-eval-source-map 那个最快

  2. 压缩 UglifyJsParallelPlugin在webpack2.0以后支持并行,就可以弃用

  3. 开启自动刷新(iframe会比inline快一点)和热更新热替换(hot 开启) 优化就是把node_modules 排除

  4. 区分环境

if (isDev) {
    config.plugins.push(new webpack.NamedModulesPlugin()); //显示模块更新名字
    config.plugins.push(new webpack.HotModuleReplacementPlugin()); //hot更新插件

    config.devServer = {
        hot: true,
        contentBase: './',
        historyApiFallback: {
            index: "/build/index.html"
        },
        publicPath: '/build/',
        host: '0.0.0.0'
    };
    config.devtool = 'eval';
} else {
    config.plugins.push(new webpack.optimize.UglifyJsPlugin({ // 开启压缩
        cache: true,
        parallel: true,
        compress: {
            warning: fasle,// 是否删除一个警告信息fasle删除
            drop_console: true,//删除console
            collapse_vars: true,是否内嵌虽然已定义但是只用到一次的变量。
            reduce_vars: true ,提取多次的变量
        },
        output: {
            comments: false,//是否删除注释,默认是不删除,设置为false,删除所有的注释
            beautify: fasle, //保留空格和制表符  建议false关闭,最紧凑的输出
        }
        
        
        
    }));

    config.devtool = '#source-map';
}

8、压缩css 在css-loader开启minimize选项 和 提取css到单独的文件

{
    test: /.(css|less)$/,
    loader: ExtractTextPlugin.extract({
        fallback: 'style-loader',
        use: 'happypack/loader?id=css'
    })
},
new ExtractTextPlugin('css/[name].[contenthash:8].css')

9、在webapck中接入cdn

  • 在output.publicPath 中设置javascript的地址
  • 在css-loader.publicPath 中设置css文件中导入的资源的地址
  • 在HtmlWebpackPlugin或者WebPlugin.stylePublicPath 设置css单独是文件的资源

10、Tree Shaking 去除重复代码 ,webapck没有程序流分析,避免不了babel产生的副作用

11、以模块化来引入

// 原来的引入方式
 import {debounce} from 'lodash';

//按模块化的引入方式
import debounce from 'lodash/debounce';

12、使用异步的模块加载
require.ensure来设置哪些模块需要异步加载,webpack会将它打包到一个独立的chunk中

$('.bg-input').click(() => {
    console.log('clicked, loading async.js')

    require.ensure([], require => {

        require('./components/async2').log();
        require('./components/async1').log();
        console.log('loading async.js done');
    });
});

import(*) 是新的按需加载,在react-router4中不能使用require.ensure要使用getAsyncComponent函数

component = {
    getAsyncComponent(() => {
        import('./pages/login')
    })
}

13、Scope Hoisting 作用域提升 webpack3的新功能

使用ModuleConcatenationPlugin插件来加快JS执行速度
这是webpack3的新特性(Scope Hoisting),其实是借鉴了Rollup打包工具来的,它将一些有联系的模块,放到一个闭包函数里面去,通过减少闭包函数数量从而加快JS的执行速度

new webpack.optimize.ModuleConcatenationPlugin({

        })

原理:原理其实很简单,分析模块之间的依赖关系,尽可能将被打散的模块合并到一个函数中,前提是不能造成代码冗余,源码必须采用es6语句。

14、提取公共代码
使用CommonsChunkPlugin提取公共的模块,可以减少文件体积,也有助于浏览器层的文件缓存,还是比较推荐的,那个是在最后打包的时候用的。

// 提取公共模块文件
        new webpack.optimize.CommonsChunkPlugin({
            chunks: ['home', 'detail'],
            // 开发环境下需要使用热更新替换,而此时common用chunkhash会出错,可以直接不用hash
            filename: '[name].js' + (isProduction ? '?[chunkhash:8]' : ''),
            name: 'common'
        }),




// 切合公共模块的提取规则,有时后你需要明确指定默认放到公共文件的模块
// 文件入口配置
    entry: {
        home: './src/js/home',
        detail: './src/js/detail',
        // 提取jquery入公共文件
        common: ['jquery', 'react', 'react-dom']
    },
    
     entry: {index:'./src/index.js',vendor: ['react','react-dom','react-router']},
    
    new webpack.optimize.CommonsChunkPlugin({
          name: "vendor",
          filename: "vendor.js",
      }),
    

15、prepack

利用抽象语法树(AST)来分析源码,过来问题大大,不建议使用

const PrepackWebpackPlugin = require('prepack-webapck-plugin').default;

module.exports = {
    plugins: [
        new PrepackWebpackPlugin()
    ]

}

16、可视化的输出分析 Analyse webpack-bundle-analyzer

17、pwa构建

用 Webpack 构建接入 Service Workers 的离线应用要解决的关键问题在于如何生成上面提到的 sw.js 文件, 并且sw.js文件中的 cacheFileList 变量,代表需要被缓存文件的 URL 列表,需要根据输出文件列表所对应的 URL 来决定,而不是像上面那样写成静态值。

假如构建输出的文件目录结构为:

├── app_4c3e186f.js
├── app_7cc98ad0.css
└── index.html

那么 sw.js 文件中 cacheFileList 的值应该是:

var cacheFileList = [
  '/index.html',
  'app_4c3e186f.js',
  'app_7cc98ad0.css'
];

Webpack 没有原生功能能完成以上要求,幸好庞大的社区中已经有人为我们做好了一个插件 serviceworker-webpack-plugin 可以方便的解决以上问题。 使用该插件后的 Webpack 配置如下:

const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const { WebPlugin } = require('web-webpack-plugin');
const ServiceWorkerWebpackPlugin = require('serviceworker-webpack-plugin');

module.exports = {
  entry: {
    app: './main.js'// Chunk app 的 JS 执行入口文件
  },
  output: {
    filename: '[name].js',
    publicPath: '',
  },
  module: {
    rules: [
      {
        test: /.css/,// 增加对 CSS 文件的支持
        // 提取出 Chunk 中的 CSS 代码到单独的文件中
        use: ExtractTextPlugin.extract({
          use: ['css-loader'] // 压缩 CSS 代码
        }),
      },
    ]
  },
  plugins: [
    // 一个 WebPlugin 对应一个 HTML 文件
    new WebPlugin({
      template: './template.html', // HTML 模版文件所在的文件路径
      filename: 'index.html' // 输出的 HTML 的文件名称
    }),
    new ExtractTextPlugin({
      filename: `[name].css`,// 给输出的 CSS 文件名称加上 Hash 值
    }),
    new ServiceWorkerWebpackPlugin({
      // 自定义的 sw.js 文件所在路径
      // ServiceWorkerWebpackPlugin 会把文件列表注入到生成的 sw.js 中
      entry: path.join(__dirname, 'sw.js'),
    }),
  ],
  devServer: {
    // Service Workers 依赖 HTTPS,使用 DevServer 提供的 HTTPS 功能。
    https: true,
  }
};

18、多页

AutoWebPlugin插件来配置多页
├── pages
│   ├── index
│   │   ├── index.css // 该页面单独需要的 CSS 样式
│   │   └── index.js // 该页面的入口文件
│   └── login
│       ├── index.css
│       └── index.js
├── common.css // 所有页面都需要的公共 CSS 样式
├── google_analytics.js
├── template.html
└── webpack.config.js

  • 所有单页应用的代码都需要放到一个目录下,例如都放在 pages 目录下;
  • 一个单页应用一个单独的文件夹,例如最后生成的 index.html 相关的代码都在 index 目录下,login.html 同理;
  • 每个单页应用的目录下都有一个 index.js 文件作为入口执行文件。

AutoWebPlugin 强制性的规定了项目部分的目录结构,在pages下,每一个文件夹就是一个目录。通过插件自动生成手动需要配置的两个html插件。

const { AutoWebPlugin } = require('web-webpack-plugin');

// 使用本文的主角 AutoWebPlugin,自动寻找 pages 目录下的所有目录,把每一个目录看成一个单页应用
const autoWebPlugin = new AutoWebPlugin('pages', {
  template: './template.html', // HTML 模版文件所在的文件路径
  postEntrys: ['./common.css'],// 所有页面都依赖这份通用的 CSS 样式文件
  // 提取出所有页面公共的代码
  commonsChunk: {
    name: 'common',// 提取出公共代码 Chunk 的名称
  },
});

module.exports = {
  // AutoWebPlugin 会为寻找到的所有单页应用,生成对应的入口配置,
  // autoWebPlugin.entry 方法可以获取到所有由 autoWebPlugin 生成的入口配置
  entry: autoWebPlugin.entry({
    // 这里可以加入你额外需要的 Chunk 入口
  }),
  plugins: [
    autoWebPlugin,
  ],
};

template.html 模版文件如下:

<html>
<head>
  <meta charset="UTF-8">
  <!--在这注入该页面所依赖但没有手动导入的 CSS-->
  <!--STYLE-->
  <!--注入 google_analytics 中的 JS 代码-->
  <script src="./google_analytics.js?_inline"></script>
  <!--异步加载 Disqus 评论-->
  <script src="https://dive-into-webpack.disqus.com/embed.js" async></script>
</head>
<body>
<div id="app"></div>
<!--在这注入该页面所依赖但没有手动导入的 JavaScript-->
<!--SCRIPT-->
<!--Disqus 评论容器-->
<div id="disqus_thread"></div>
</body>
</html>
在模版中生成
  • CSS 类型的文件注入到 所在的位置,如果 不存在就注入到 HTML HEAD 标签的最后;
  • JavaScrip 类型的文件注入到 所在的位置,如果 不存在就注入到 HTML BODY 标签的最后。
通过gulp来管理html,webapck来处理js也是解决多页的好方法
利用html-webpack-plugin 插件循环生成
// 引入插件
const HTMLWebpackPlugin = require("html-webpack-plugin");
// 引入多页面文件列表
const { HTMLDirs } = require("./config");
// 通过 html-webpack-plugin 生成的 HTML 集合
let HTMLPlugins = [];
// 入口文件集合
let Entries = {}

// 生成多页面的集合
HTMLDirs.forEach((page) => {
    const htmlPlugin = new HTMLWebpackPlugin({
        filename: `${page}.html`,
        template: path.resolve(__dirname, `../app/html/${page}.html`),
        chunks: [page, 'commons'],
    });
    HTMLPlugins.push(htmlPlugin);
    Entries[page] = path.resolve(__dirname, `../app/js/${page}.js`);
})

19、同构

构建服务端渲染

服务端渲染的代码要运行在nodejs环境,和浏览器不同的是,服务端渲染代码需要采用commonjs规范同时不应该包含除js之外的文件比如css。webpack配置如下:

module.exports = {
  target: 'node',
  entry: {
    'server_render': './src/server_render',
  },
  output: {
    filename: './dist/server/[name].js',
    libraryTarget: 'commonjs2',
  },
  module: {
    rules: [
      {
        test: /.js$/,
        loader: 'babel-loader',
      },
      {
        test: /.(scss|css|pdf)$/,
        loader: 'ignore-loader',
      },
    ]
  },
};

其中几个关键的地方在于:

target: 'node' 指明构建出的代码是要运行在node环境里
libraryTarget: 'commonjs2' 指明输出的代码要是commonjs规范
{test: /.(scss|css|pdf)$/,loader: 'ignore-loader'} 是为了防止不能在node里执行服务端渲染也用不上的文件被打包进去。

20、laoder与plugins插件编写

如果你的扩展是想对一个个单独的文件进行转换那么就编写loader剩下的都是plugin

其中对文件进行转换可以是像:

  • babel-loader把es6转换成es5
  • file-loader把文件替换成对应的URL
  • raw-loader注入文本文件内容到代码里去

编写loader非常简单,以comment-require-loader为例:

module.exports = function (content) {
    return replace(content);
};

loader的入口需要导出一个函数,这个函数要干的事情就是转换一个文件的内容。
函数接收的参数content是一个文件在转换前的字符串形式内容,需要返回一个新的字符串形式内容作为转换后的结果,所有通过模块化倒入的文件都会经过loader。从这里可以看出loader只能处理一个个单独的文件而不能处理代码块

class EndWebpackPlugin {

constructor(doneCallback, failCallback) {
    this.doneCallback = doneCallback;
    this.failCallback = failCallback;
}

apply(compiler) { 汇编者; 编辑者; 编纂者 
    // 监听webpack生命周期里的事件,做相应的处理
    compiler.plugin('done', (stats) => {
        this.doneCallback(stats);
    });
    compiler.plugin('failed', (err) => {
        this.failCallback(err);
    });
}

}

module.exports = EndWebpackPlugin;
loader的入口需要导出一个class, 在new EndWebpackPlugin()的时候通过构造函数传入这个插件需要的参数,在webpack启动的时候会先实例化plugin再调用plugin的apply方法,插件需要在apply函数里监听webpack生命周期里的事件,做相应的处理。
webpack plugin 里有2个核心概念:

Compiler: 从webpack启动到推出只存在一个Compiler,Compiler存放着webpack配置
Compilation: 由于webpack的监听文件变化自动编译机制,Compilation代表一次编译。
Compiler 和 Compilation 都会广播一系列事件。
webpack生命周期里有非常多的事件可以在event-hooks和Compilation里查到

21、常用loader与plugins

加载文件
  • raw-loader:把文本文件的内容加载到代码中去,在 3-20加载SVG 中有介绍。
  • file-loader:把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件,在 3-19加载图片、3-20加载 SVG、4-9 CDN 加速 中有介绍。
  • url-loader:和 file-loader 类似,但是能在文件很小的情况下以 base64 的方式把文件内容注入到代码中去,在 3-19加载图片、3-20加载 SVG 中有介绍。
  • source-map-loader:加载额外的 Source Map 文件,以方便断点调试,在 3-21加载 Source Map 中有介绍。
  • svg-inline-loader:把压缩后的 SVG 内容注入到代码中,在 3-20加载 SVG 中有介绍。
  • node-loader:加载 Node.js 原生模块 .node 文件。
  • image-loader:加载并且压缩图片文件。
  • json-loader:加载 JSON 文件。
  • yaml-loader:加载 YAML 文件。
编译模版
  • pug-loader:把 Pug 模版转换成 JavaScript 函数返回。
  • handlebars-loader:把 Handlebars 模版编译成函数返回。
  • ejs-loader:把 EJS 模版编译成函数返回。
  • haml-loader:把 HAML 代码转换成 HTML。
  • markdown-loader:把 Markdown 文件转换成 HTML。
转换脚本语言
  • babel-loader:把 ES6 转换成 ES5,在3-1使用 ES6 语言中有介绍。
  • ts-loader:把 TypeScript 转换成 JavaScript,在3-2使用 TypeScript 语言中有遇到。
  • awesome-typescript-loader:把 TypeScript 转换成 JavaScript,性能要比 ts-loader 好。
  • coffee-loader:把 CoffeeScript 转换成 JavaScript。
转换样式文件
  • css-loader:加载 CSS,支持模块化、压缩、文件导入等特性。
  • style-loader:把 CSS 代码注入到 JavaScript 中,通过 DOM 操作去加载 CSS。
  • sass-loader:把 SCSS/SASS 代码转换成 CSS,在3-4使用 SCSS 语言中有介绍。
  • postcss-loader:扩展 CSS 语法,使用下一代 CSS,在3-5使用 PostCSS中有介绍。
  • less-loader:把 Less 代码转换成 CSS 代码。
  • stylus-loader:把 Stylus 代码转换成 CSS 代码。
检查代码
  • eslint-loader:通过 ESLint 检查 JavaScript 代码,在 3-16检查代码中有介绍。
  • tslint-loader:通过 TSLint 检查 TypeScript 代码。
  • mocha-loader:加载 Mocha 测试用例代码。
  • coverjs-loader:计算测试覆盖率。
其它
  • vue-loader:加载 Vue.js 单文件组件,在3-7使用 Vue 框架中有介绍。
  • i18n-loader:加载多语言版本,支持国际化。
  • ignore-loader:忽略掉部分文件,在3-11构建同构应用中有介绍。
  • ui-component-loader:按需加载 UI 组件库,例如在使用 antd UI 组件库时,不会因为只用到了 Button 组件而打包进所有的组件。
用于修改行为
  • define-plugin:定义环境变量,在4-7区分环境中有介绍。
  • context-replacement-plugin:修改 require 语句在寻找文件时的默认行为。
  • ignore-plugin:用于忽略部分文件。
用于优化
  • commons-chunk-plugin:提取公共代码,在4-11提取公共代码中有介绍。
  • extract-text-webpack-plugin:提取 JavaScript 中的 CSS 代码到单独的文件中,在1-5使用 Plugin 中有介绍。
  • prepack-webpack-plugin:通过 Facebook 的 Prepack 优化输出的 JavaScript 代码性能,在 4-13使用 Prepack 中有介绍。
  • uglifyjs-webpack-plugin:通过 UglifyES 压缩 ES6 代码,在 4-8压缩代码中有介绍。
  • webpack-parallel-uglify-plugin:多进程执行 UglifyJS 代码压缩,提升构建速度。
  • imagemin-webpack-plugin:压缩图片文件。
  • webpack-spritesmith:用插件制作雪碧图。
  • ModuleConcatenationPlugin:开启 Webpack Scope Hoisting 功能,在4-14开启 ScopeHoisting中有介绍。
  • dll-plugin:借鉴 DDL 的思想大幅度提升构建速度,在4-2使用 DllPlugin中有介绍。
  • hot-module-replacement-plugin:开启模块热替换功能。
其它
  • serviceworker-webpack-plugin:给网页应用增加离线缓存功能,在3-14 构建离线应用中有介绍。
  • stylelint-webpack-plugin:集成 stylelint 到项目中,在3-16检查代码中有介绍。
  • i18n-webpack-plugin:给你的网页支持国际化。
  • provide-plugin:从环境中提供的全局变量中加载模块,而不用导入对应的文件。
  • web-webpack-plugin:方便的为单页应用输出 HTML,比 html-webpack-plugin 好用。

22 webpack4

  • webpack 4 更快(速度提升98%)
  • 新增Mode选项,区分环境
  • 零配置以及默认值
  • 再见 CommonsChunkPlugin =》 新 API optimize.splitChunks
  • WebAssembly 支持
  • .mjs 支持
原文地址:https://www.cnblogs.com/chenjinxinlove/p/8467478.html