2018-09-29

libraryTarget 和 library


当用 Webpack 去构建一个可以被其他模块导入使用的库时需要用到它们。

  • output.libraryTarget 配置以何种方式导出库
  • output.library 配置导出库的名称 output.libraryTarget 是字符串的枚举类型,支持以下配置

1.1 var (默认)

编写的库将通过var被赋值给通过library指定名称的变量。

1.1.1 index.js [#](#t21.1.1 index.js)

module.exports =  {
    add(a,b) {
        return a+b;
    }
}

1.1.2 bundle.js [#](#t31.1.2 bundle.js)

var calculator=(function (modules) {}({})

1.1.3 index.html [#](#t41.1.3 index.html)

    <script src="bundle.js"></script>
    <script>
        let ret = calculator.add(1,2);
        console.log(ret);
    </script>

1.2 commonjs [#](#t51.2 commonjs)

编写的库将通过 CommonJS 规范导出。

1.2.1 导出方式 [#](#t61.2.1 导出方式)

exports["calculator"] = (function (modules) {}({})

1.2.2 使用方式 [#](#t71.2.2 使用方式)

require('npm-name')['calculator'].add(1,2);

npm-name是指模块发布到 Npm 代码仓库时的名称

1.3 commonjs2 [#](#t81.3 commonjs2)

编写的库将通过 CommonJS 规范导出。

1.3.1 导出方式 [#](#t91.3.1 导出方式)

module.exports = (function (modules) {}({})

1.3.2 使用方式 [#](#t101.3.2 使用方式)

require('npm-name').add();

在 output.libraryTarget 为 commonjs2 时,配置 output.library 将没有意义。

1.4 this [#](#t111.4 this)

编写的库将通过 this 被赋值给通过 library 指定的名称,输出和使用的代码如下:

1.4.1 导出方式 [#](#t121.4.1 导出方式)

this["calculator"]= (function (modules) {}({})

1.4.2 使用方式 [#](#t131.4.2 使用方式)

this.calculator.add();

1.5 window [#](#t141.5 window)

编写的库将通过 window 被赋值给通过 library 指定的名称,即把库挂载到 window 上,输出和使用的代码如下:

1.5.1 导出方式 [#](#t151.5.1 导出方式)

window["calculator"]= (function (modules) {}({})

1.5.2 使用方式 [#](#t161.5.2 使用方式)

window.calculator.add();

1.6 global [#](#t171.6 global)

编写的库将通过 global 被赋值给通过 library 指定的名称,即把库挂载到 global 上,输出和使用的代码如下:

1.6.1 导出方式 [#](#t181.6.1 导出方式)

global["calculator"] = (function (modules) {}({})

1.6.2 使用方式 [#](#t191.6.2 使用方式)

global.calculator.add();

2.DLL #

.dll为后缀的文件称为动态链接库,在一个动态链接库中可以包含给其他模块调用的函数和数据

  • 把基础模块独立出来打包到单独的动态连接库里
  • 当需要导入的模块在动态连接库里的时候,模块不能再次被打包,而是去动态连接库里获取
  • dll-plugin

2.1 定义Dll [#](#t212.1 定义Dll)

  • DllPlugin插件: 用于打包出一个个动态连接库

  • DllReferencePlugin: 在配置文件中引入DllPlugin插件打包好的动态连接库

    const path=require('path');
    const DllPlugin=require('webpack/lib/DllPlugin');
    module.exports={
        entry: {
            react:['react','react-dom']
        },// 把 React 相关模块的放到一个单独的动态链接库
        output: {
            path: path.resolve(__dirname,'dist'),// 输出的文件都放到 dist 目录下
            filename: '[name].dll.js',//输出的动态链接库的文件名称,[name] 代表当前动态链接库的名称
            library: '_dll_[name]',//存放动态链接库的全局变量名称,例如对应 react 来说就是 _dll_react
        },
        plugins: [
            new DllPlugin({
                // 动态链接库的全局变量名称,需要和 output.library 中保持一致
                // 该字段的值也就是输出的 manifest.json 文件 中 name 字段的值
                // 例如 react.manifest.json 中就有 "name": "_dll_react"
                name: '_dll_[name]',
                // 描述动态链接库的 manifest.json 文件输出时的文件名称
                path: path.join(__dirname, 'dist', '[name].manifest.json')
            })
        ]
    }
    
    webpack --config webpack.dll.config.js --mode development
    

2.2 使用动态链接库文件 [#](#t222.2 使用动态链接库文件)

const DllReferencePlugin = require('webpack/lib/DllReferencePlugin')
plugins: [
  new DllReferencePlugin({
    manifest:require('./dist/react.manifest.json')
  })
]
webpack --config webpack.config.js --mode development

2.3 html中使用 [#](#t232.3 html中使用)

<script src="react.dll.js"></script>
<script src="bundle.js"></script>

3. HappyPack [#](#t243. HappyPack)

  • 构建需要解析和处理文件,文件读写和计算密集型的操作太多后速度会很慢

  • Node.js 之上的 Webpack 是单线程模型

  • HappyPack就能让Webpack把任务分解给多个子进程去并发的执行,子进程处理完后再把结果发送给主进程。 happypack

    npm i happypack@next -D
    
    const HappyPack = require('happypack');
        rules: [
        {
            test: /\.js$/,
            // 把对 .js 文件的处理转交给 id 为 babel 的 HappyPack 实例
            use: ['happypack/loader?id=babel'],
            exclude: path.resolve(__dirname, 'node_modules'),
        },
        {
            test: /\.css$/,
            // 把对 .css 文件的处理转交给 id 为 css 的 HappyPack 实例
            use: ['happypack/loader?id=css']
        }
    ]
    new HappyPack({
        //用唯一的标识符 id 来代表当前的 HappyPack 是用来处理一类特定的文件
        id: 'babel',
        // 如何处理 .js 文件,用法和 Loader 配置中一样
        use:[{
                loader: 'babel-loader',
                    options: {
                    presets:['env','stage-0','react']
                }
            }]
    }),
    new HappyPack({
        //用唯一的标识符 id 来代表当前的 HappyPack 是用来处理一类特定的文件
        id: 'css',
        // 如何处理 .css 文件,用法和 Loader 配置中一样
        use:['style-loader','css-loader'],
        threads: 4, //代表开启几个子进程去处理这一类型的文件
        verbose: true //是否允许输出日志
    })
    

4. CDN [#](#t254. CDN)

CDN 又叫内容分发网络,通过把资源部署到世界各地,用户在访问时按照就近原则从离用户最近的服务器获取资源,从而加速资源的获取速度。

  • HTML文件不缓存,放在自己的服务器上,关闭自己服务器的缓存,静态资源的URL变成指向CDN服务器的地址

  • 静态的JavaScript、CSS、图片等文件开启CDN和缓存,并且文件名带上HASH值

  • 为了并行加载不阻塞,把不同的静态资源分配到不同的CDN服务器上

    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: '[name]_[hash:8].js',
        publicPath: 'http://img.zhangximufeng.cn'
    },
    

5.Tree Shaking [#](#t265.Tree Shaking)

Tree Shaking 可以用来剔除JavaScript中用不上的死代码。它依赖静态的ES6模块化语法,例如通过importexport导入导出。

5.1. 不要编译ES6模块 [#](#t275.1. 不要编译ES6模块)

use:[{
  loader: 'babel-loader',
  options: {
 		 presets:[['env',{modules: false }],'stage-0','react']
  }
}]

5.2 显示未使用的导出实例 [#](#t285.2 显示未使用的导出实例)

npx webpack --display-used-exports

6.提取公共代码 #

6.1 为什么需要提取公共代码 [#](#t306.1 为什么需要提取公共代码)

大网站有多个页面,每个页面由于采用相同技术栈和样式代码,会包含很多公共代码,如果都包含进来会有问题

  • 相同的资源被重复的加载,浪费用户的流量和服务器的成本;
  • 每个页面需要加载的资源太大,导致网页首屏加载缓慢,影响用户体验。 如果能把公共代码抽离成单独文件进行加载能进行优化,可以减少网络传输流量,降低服务器成本

6.2 如何提取 [#](#t316.2 如何提取)

6.3 提取公共代码 [#](#t326.3 提取公共代码)

    entry: {
        pageA: './src/pageA',
        pageB: './src/pageB',
        pageC: './src/pageC'
    },
    output: {
        path: path.resolve(__dirname,'dist'),
        filename: '[name].js'
    },
    optimization: {
        splitChunks: {
            cacheGroups: {
                commons: {
                    chunks: "initial",
                    minChunks: 2,//最小重复的次数
                    minSize: 0//最小提取字节数
                },
                vendor: {
                    test: /node_modules/,
                    chunks: "initial",
                    name: "vendor",
                }
            }
        }
    }

7.开启 Scope Hoisting [#](#t337.开启 Scope Hoisting)

Scope Hoisting 可以让 Webpack 打包出来的代码文件更小、运行的更快, 它又译作 "作用域提升",是在 Webpack3 中新推出的功能。

  • 代码体积更小,因为函数申明语句会产生大量代码
  • 代码在运行时因为创建的函数作用域更少了,内存开销也随之变小

7.1 插件配置 [#](#t347.1 插件配置)

const ModuleConcatenationPlugin = require('webpack/lib/optimize/ModuleConcatenationPlugin');
module.exports = {
  resolve: {
    // 针对 Npm 中的第三方模块优先采用 jsnext:main 中指向的 ES6 模块化语法的文件
    mainFields: ['jsnext:main', 'browser', 'main']
  },
  plugins: [
    // 开启 Scope Hoisting
    new ModuleConcatenationPlugin(),
  ],
};

7.2 代码 [#](#t357.2 代码)

hello.js

export default 'Hello';

index.js

import str from './hello.js';
console.log(str);

输出的结果main.js

var n = name = "zxmf";
  console.log(n)

函数由两个变成了一个,hello.js 中定义的内容被直接注入到了 main.js 中

8.代码分离 #

  • 代码分离是 webpack 中最引人注目的特性之一。
  • 此特性能够把代码分离到不同的 bundle 中,然后可以按需加载或并行加载这些文件。 有三种常用的代码分离方法:
    • 入口起点:使用entry配置手动地分离代码。
    • 防止重复:使用splitChunks去重和分离chunk
    • 动态导入:通过模块的内联函数调用来分离代码。

8.1 多个入口 [#](#t378.1 多个入口)

entry: {
  index: './src/index.js',
  another: './src/another-module.js'
}

8.2 防止重复 [#](#t388.2 防止重复)

splitChunks可以将公共的依赖模块提提取到一个新生成的 chunk. common-chunk-and-vendor-chunk

optimization: {
 splitChunks: {
     cacheGroups: {
         commons: {
             chunks: "initial",
             minChunks: 2,
             minSize:0
         },
        vendor: {
             test: /node_modules/,
             chunks: "initial",
             name: "vendor",
         }
 }
}

8.3 动态导入和懒加载(dynamic imports) [#](#t398.3 动态导入和懒加载(dynamic imports))

用户当前需要用什么功能就只加载这个功能对应的代码,也就是所谓的按需加载 在给单页应用做按需加载优化时,一般采用以下原则:

  • 对网站功能进行划分,每一类一个chunk
  • 对于首次打开页面需要的功能直接加载,尽快展示给用户
  • 某些依赖大量代码的功能点可以按需加载
  • 被分割出去的代码需要一个按需加载的时机

8.3.1 handler.js [#](#t408.3.1 handler.js)

module.exports=function () {
    alert('你点我啦!');
}

8.3.2 index.js [#](#t418.3.2 index.js)

document.querySelector('#clickBtn').addEventListener('mouseover',() => {
    import('./handler').then(clickMe => {
        window.clickMe=clickMe.default;
    });
});

8.3.3 html [#](#t428.3.3 html)

<div id="clickBtn" onclick="clickMe()">弹框</div>

8.4 react-router4 路由懒加载 [#](#t438.4 react-router4 路由懒加载)

8.4.1 index.js [#](#t448.4.1 index.js)

import React from 'react';
import ReactDOM from 'react-dom';
import {HashRouter as Router,Route} from 'react-router-dom';
import Bundle from './Bundle';
let LazyAbout=(props) => (<Bundle {...props} load={()=>import('./About')}/>)
let Home=() => <div>Home</div>
ReactDOM.render(
<Router>
    <div>
      <Route path="/" component={Home} />
      <Route path="/about" component={LazyAbout}/>
    </div>
</Router>,document.getElementById('root'));

8.4.2 Bundle [#](#t458.4.2 Bundle)

import React from 'react';
export default class Bundle extends React.Component{
    state={Mod: null}
    componentWillMount() {
        this.props.load().then(mod=>this.setState({Mod: mod.default? mod.default:mod}));
    }
    render() {
        let Mod=this.state.Mod;
        return Mod && <Mod  {...this.props}/>;
    }
}

8.4.2 Bundle [#](#t468.4.2 Bundle)

import React from 'react';
export default props => <div>About</div>

9.用 HMR 提高开发效率 [#](#t479.用 HMR 提高开发效率)

  • HMR 全称是 Hot Module Replacement,即模块热替换

  • Hot Reloading,当代码变更时通知浏览器刷新页面,以避免频繁手动刷新浏览器页面

  • HMR 可以理解为增强版的 Hot Reloading,但不用整个页面刷新,而是局部替换掉部分模块代码并且使其生效

    image-20180820125843371

webpack.config.js

const webpack = require('webpack')
module.exports = {
  devServer: {
    hot: true // dev server 的配置要启动 hot,或者在命令行中带参数开启
  },
  plugins: [
    new webpack.NamedModulesPlugin(), // 用于启动 HMR 时可以显示模块的相对路径
    new webpack.HotModuleReplacementPlugin(), // Hot Module Replacement 的插件
  ],
}

hello.js

module.exports = function () {
    console.log('hello');
}
let hello = require('./hello');
hello();
if (module.hot) {
    module.hot.accept('./hello', function() {
        let hello = require('./hello');
        hello();
    })
}

9. 输出分析 [#](#t489. 输出分析)

webpack --profile --json > stats.json
  • profile:记录下构建过程中的耗时信息;

  • json:以 JSON 的格式输出构建结果,最后只输出一个 .json 文件,这个文件中包括所有构建相关的信息。

  • Webpack 官方提供了一个可视化分析工具 Webpack Analyse

  • Modules:展示所有的模块,每个模块对应一个文件。并且还包含所有模块之间的依赖关系图、模块路径、模块ID、模块所属 Chunk、模块大小;

  • Chunks:展示所有的代码块,一个代码块中包含多个模块。并且还包含代码块的ID、名称、大小、每个代码块包含的模块数量,以及代码块之间的依赖关系图;

  • Assets:展示所有输出的文件资源,包括 .js、.css、图片等。并且还包括文件名称、大小、该文件来自哪个代码块;

  • Warnings:展示构建过程中出现的所有警告信息;

  • Errors:展示构建过程中出现的所有错误信息;

  • Hints:展示处理每个模块的过程中的耗时。

Gitalking ...

Markdown is supported

Be the first guy leaving a comment!