模块化

模块化分为AMD、CMD、CommonJS、ES6四类

一、CommonJS 

    Node、webpack使用的CommonJS规范, require加载是同步的

    // 导入
    const moduleA = require('./moduleA');
    moduleA.someFun(1,2);            // 需要等moduleA加载后在执行,这里是同步

    // 导出
    module.exports = moduleA.someFunc


二、AMD 异步加载模块

    可异步加载依赖模块,可并行加载多个依赖,可以运行浏览器和node环境下,缺点是需要加载AMD的库才能使用,常用的require.js

    // 定义模块
    define('moduleA', [依赖文件], function(jquery){
            var someFun = (x, y) => { return x + y}
            return {
                someFun,
            }
    })

    // 导入和使用
    require(['moduleA'], function(moduleA){
        module.someFun(1,2);
    })


三、ES6

    JS的标准化模块加载,会逐渐的取代commonJS和AMD

    // 导入
    import React from 'react'
    import ReactDOM, { render } from 'react-dom';

    // 导出
    export function hello(){ ... }
    export default {
        ...
    }
    export { fn1, fn2 }


四、CMD 同步加载模块

    代表seajs

    // 定义模块
    define(function(require,exports,module){
        var $ = require('jquery');        // 用来加载模块

        exports.sayHello = function(){    // 导出的接口
            ...
        }
    });

构建工具的什么用

1、代码转换 - 将ES6转ES5, 将SCSS转成css

2、文件优化 - 压缩JS、CSS、HTML代码,压缩合并图片

3、代码分割 - 提取多个页面的公共代码,提取首屏不需要执行代码,其它的异步加载

4、模块合并 - 将多个模块合并成一个文件

5、自动刷新 - 监听本地代码变化,并自动构建和刷新浏览器

6、代码校验 - 校验代码是否符合规范

7、自动发布 - 自动构建出线上发布代码并传输给发布系统

webpack安装和命令

$ npm i -D webpack webpack-cli         // 安装稳定版本,4版本以上需要装webpack-cli, -D是--save-dev的缩写

    npm i -D webpack@3.10.0                  // 安装指定版本

$ touch webpack.config.js // 创建配置文件

一、配置npm运行命令

    "scripts": {
        "start": "webpack --config webpack.config.js"
    }
    $ npm start         // 执行


二、webpack命令

    1、webpack            // 最基本的启动webpack命令

    2、webpack --config XXX.js   // 如果默认的配置文件不叫webpack.config.js,就需要在这指定配置文件的名称

        script: {
            "build": "webpack --config production.config.js",
            "dev": "webpack --config development.config.js"
        }

    3、webpack -w         // watch方法,时实预览进行打包 相当于 --watch 

    4、webpack -p         // 对打包后的文件进行压缩

    5、webpack -d         // 提供SourceMaps,方便调试

    6、webpack --colors     // 输出结果带彩色,比如: 会用红色显示耗时较长的步骤

    7、webpack --profile // 输出性能数据,可以看到每一步的耗时

    8、webpack --display-modules     // 默认情况下 node_modules 下的模块会被隐藏,加上这个参数可以显示这些被隐藏的模块

    9、webpack --progress --colors   // 展示一些进度条,同时增加颜色

    10、webpack --display-error-details    // 打印出错在哪个文件和行

Entry

entry是配置模块的入口,执行构建开始时递归解析所有的入口依赖,配置值: string | object | array

// 单入口
module.exports = {
    entry: './src/main.js'
}

// 多入口
module.exports = {
    entry: {
        main: ["./main.js", "./list.js"]
    }
}

// 多入口,app(应用主入口),vendors(公共库)入口
module.exports = {
    entry: {
        main: './src/main.js',
        list: './src/list.js',
        vendors: './src/jquery.js'                // 加载第三方模块
    }
}

Output

配置 output 选项可以控制 webpack 如何向硬盘写入编译文件

output的配置属性:

1、path: 输出的目录,绝对路径

2、filename: 用于输出文件的文件名

    变量: 1) id chunk 的唯一标识从0开始      2)name chunk 的名称        3)hash 输出hash值        4)chunkhash 内容的hash值

    filename: 'bundle.js'

    filename: '[name].js'            // 用于多入口

    filename: '[chunkhash].js'    // 一个随机数,入口为entry chunk

    filename: '[name].js[hash]'    // 带hash值 [hash:8] 代表8位的hash,默认20位

3、publicPath: 复杂项目里会有一些构建出的资源需要异步加载,加载这些异步资源需要对应的URL

    output: {
        filename: '[name]_[chunkhash:8].js',
        publicPath: 'https://cdn.example.com/assets/'            // 发布到线上的HTML <script src="https://cdn.example.com/assets/main.12345678.js"</script>
    }

Loader

它的基本工作流是将一个文件以字符串的形式读入,对其进行语法分析及转换, 预处理 CoffeeScript、    TypeScript、ESNext (Babel)、Sass、Less、Stylus

1、rules: 配置模块的读取和解析,将所有引用资源(.css、.html、.scss、.jpg)作为模块处理

2、条件匹配: 通过test、include、exclude三个配置项来选中loader规则文件

    test: 匹配哪些文件

    include: 指定一个目录下的所有文件,可以加快webpack的搜索速度

    exclude: 排除某个目录,不在这个目录搜索文件

3、应用规则: 对选中的文件通过usr配置来应用loader

4、重置顺序: 默认从右向左执行, 通过enforce选项将其中一个loader的顺序放到最前或最后

module: {
rules: [
        {
    test: /(\.jsx|\.js)$/,
    use: {
      loader: "babel-loader",
    },
            include: [path.resolve(__dirname, 'src)],        // 只在src目录下搜索js文件
  }
  {
    test: /\.css$/,
    use: ["style-loader", "css-loader?minimize", "sass-loader"],        // minimize 压缩css
            exclude: [/node_modules/]                // 除了node_modules目录不搜索
  }
]

}

插件: npm install --save-dev css-loader

resolve

webpack启动后会从入口找出所有的依赖模块,resolve配置如何寻找模块对应的文件

1、alias 让模块引入变得简单

    import $ from '/js/lib/jquery.min.js';      开发中需要引入jquery就需要写这类路径

    通过alias来解决
    resolve: {
        alias: {
            jquery: path.resolve(__dirname, "/js/lib/jquery.min.js"),
        },
        extensions: ['', '.js', '.vue', '.scss', '.css']             //设置require或import的时候可以不需要带后缀
    }

    引用
    import $ from 'jquery';        // jquery就会被替换成依赖的路径

plugins

plugins扩展webpack功能

一、webpack自带插件,可以直接使用

    pulgins: [
        new webpack.optimize.UglifyJsPlugin(),
        new webpack.optimize.CommonsChunkPlugin({    // webpack自带插件在webpack.optimize内
            name: 'vendor',                                // 上面入口定义的节点组
            filename: 'jquery.vendor.js?[hash]'     // 最后生成的文件名
        })
    ]

二、非自身插件,需要先调用插件

    例: main.js

        require('./main.css')
        const show = require('./show.js)
        show(1,2)

    webpack.config.js
    // 将引用的css文件提取出来 
    const ExtractTextPlugin = require('extract-text-webpack-plugin');        

    pulgins: [
        new ExtractTextPlugin({
            filename: '[name].[contenthash:8].css',            // main.11ad8d2c.css
            allChunks: true,
        })
    ]

webpack插件列表: https://www.webpackjs.com/plugins/

devServer

webpack-dev-server - 创建一个本地服务,并能设置代理服务,并且能够实时重新加载

一、安装webpack-dev-server

$ npm i --save-dev webpack-dev-server

二、创建文件

// 创建项目目录
$ mkdir webpack_base_demo && cd webpack_base_demo      

// 创建配置文件
$ touch README.md  .gitignore  .babelrc  webpack.config.js

// 创建package.json
$ npm init 

// 配置.babelrc文件
{
    "presets": ["react", "env"],
    "env": {
        "development": {
        <!-- "plugins": [["react-transform", {
            "transforms": [{
                "transform": "react-transform-hmr",
                "imports": ["react"],
                "locals": ["module"]
            }]
        }]] -->
        }
    }
}

三、安装需要的loader插件

// 如果使用react框架来做项目,先下载包
    $ npm i --save react react-dom

// babel的插件,在react热更新
    $ npm i --save babel-plugin-react-transform react-transform-hmr   

// 使用Babel-loader来解析es6和react
    $ npm i --save babel-core babel-loader babel-preset-env babel-preset-react

    // vue-load - 解析和转换.vue文件,vue-template-compiler - 将vue-load提取出的HTML模板编译成可执行的JS代码,vue项目可直接安装vue-cli
    $ npm vue-load vue-template-compiler

    // jsx转换
    $ npm i --save jsx-loader

    // css模块化,可以在组件中引用指定模块的css文件, import './main.css'
    $ npm i --save style-loader css-loader 

    // 可以将js和css中的导入的图片替换成正确的地址,同时将文件输出到对应的位置
    $ npm i --save file-loader

    // 将文件的图片经过base64编码后注入js或css中
    $ npm i --save url-loader    

    // 加载SVG
    $ npm i --save svg-loader

    // CSS预处理器  autoprefixer 自动加载前缀
    $ npm i --save less-loader sass-loader

五、package.json配置

"scripts": {
    "dev": "webpack-dev-server --hot --inline --progress",
    "start": "webpack --progress --profile --colors --config webpack.config.js",
},
$ npm run start        // 启动开发环境

六、创建生成和开发配置文件

webpack.config.js 开发环境所用配置文件
webpack.pub.config.js  生产环境所用配置文件

七、注意

1、启动两个侦听一个是webpack,一个是devServer,单独启devServer热更新不执行编译,暂没找到解决方法

  $ npm run build

  $ npm run dev

devServer的执行命令

webpack-dev-server - 在 localhost:8080 建立一个 Web 服务器

webpack-dev-server –devtool eval - 为你的代码创建源地址。当有任何报错的时候可以让你更加精确地定位到文件和行号

webpack-dev-server –progress - 显示合并代码进度

webpack-dev-server –colors - 命令行中显示颜色

webpack-dev-server –content-base build // webpack-dev-server服务会默认以当前目录伺服文件,如果设置了content-base的话,服务的根路径则为build目录

webpack-dev-server –inline 可以自动加上dev-server的管理代码,实现热更新

webpack-dev-server –hot 开启代码热替换,可以加上HotModuleReplacementPlugin

webpack-dev-server –port 3000 设置服务端口

webpack与gulp

gulp 是 task runner,Webpack 是 module bundler

webpack对多入口支持的不太好

gulp没有模块化的支持,需要与webpack结合

Source Map

启动时需要加 --devtool source-map 参数重启DevServer后刷新页面,在chrome开发者工具就可以调试源码

常用的处理g

一、css处理

    基本问题包括:

        预编译语言转换
        样式文件挂载方式选择
        代码优化(合并及压缩)
        去除或保留指定格式的注释
        资源定位路径的转换
        响应式布局单位转换【可选】
        模块化【可选】
        处理浏览器兼容【可选】

    1、style-loader - 将处理结束的CSS代码存储在js中,运行时嵌入&lt;style&gt;后挂载至html页面上

    2、css-loader - 加载器,使webpack可以识别css模块

    3、postcss-loader - 加载器,下一篇将详细描述

    4、sass-loader - 加载器,使webpack可以识别scss/sass文件,默认使用node-sass进行编译

    5、mini-css-extract-plugin - 提取单独的css文件插件,4.0版本启用的插件,替代原extract-text-webpack-plugin插件,将处理后的CSS代码提取为独立的CSS文件

    6、optimize-css-assets-webpack-plugin - 插件,实现CSS代码压缩

    7、autoprefixer - 自动化添加跨浏览器兼容前缀


二、Assets 资源(图片、json、xml)

    * 体积压缩
    * 雪碧图合并及引用修正
    * 资源的引用路径自动替换

    1、file-loader - 处理资源文件打包

    2、url-loader - 优化项目中对于资源的引用路径,并设定大小限制

    3、html-loader - html中的静态资源替换

    module: {
        rules: [
            {    
                test:/\.(jpg|png|svg|gif)/,    
                use:[{      
                    loader:'file-loader',      
                    options:{        
                        outputPath:'imgs/'
                    }
                }]
            },
            {    
                test:/\.(jpg|png|svg|gif)/,    
                use:[{      
                    loader:'url-loader',      
                    options:{        
                        limit: 8129,            // 限制图片转base64引用大小
                        fallback: 'file-loader',        // 大小limit的交给file-loader处理
                        ouputpath: 'imgs/'        // 指定输出路径
                    }
                }]
            }
        ]
    }


三、处理JS和splitChunk

    Js文件打包需求

    * 代码编译(TS或ES6代码的编译)
    * 脚本合并
    * 公共模块识别
    * 代码分割
    * 代码压缩混淆

    1、Babel转换ES6+

        webpack.config.js: 
            module: {    
                rules: [
                    {        
                        test: /\.js$/,        
                        exclude: /node_modules/,        
                        use: [
                            {             
                                loader: 'babel-loader'
                            }
                        ]
                    }
                ]
            },

        .bablerc: 
            {    
                "presets":[
                    [
                        "env",
                        {            
                            "targets":{                
                                "browsers":"last 2 versions"
                            }
                        }
                    ]
                ],    
                "plugins": [         
                    "babel-plugin-transform-runtime" 
                ]
            }

    2、脚本合并


    3、脚本分割

        为什么要分割,如果遇到比如使用Echarts第三方库或公用文件,这个时候我们不希望把它与业务一起合并到一个文件中,会使这个文件变得非常打,不利于加载


    4、代码混淆压缩

        UglifyJs


    5、splitChunks

        用于提取公用文件或第三方组件,4.0废弃了CommonsChunkPlugin插件,使用optimization.splitChunks和optimization.runtimeChunk来代替

        vendor 加载公用文件或第三方组件

        entry: {
            index: path.resolve(basePath, 'js/index.js'),
            list: path.resolve(basePath, 'js/list.js'),
            vendors:  path.resolve(basePath, 'js/common.js'),
        },

        // 使用optimization,与entry同级,不需要放到plugins内
        optimization: {
            splitChunks: {
                cacheGroups: {
                    commons: {
                        name: "vendors",
                        chunks: "initial",
                        minChunks: 2
                    }
                }
            }
        },

        plugins: [
            new HtmlWebpackPlugin({
                title: '首页', 
                template: path.resolve(basePath, 'index.html'),
                filename: path.resolve(buildPath, 'index.html'),
                chunks:['index', 'vendors'],            // 这里加vendors
                inject:'body',
            })
        ]

        <!-- module.exports = { 
            optimization: {    
                splitChunks: {      
                    chunks: 'async',                        // 默认只作用于异步模块,为`all`时对所有模块生效,`initial`对同步模块有效
                    minSize: 30000,                            // 合并前模块文件的体积
                    minChunks: 1,                                // 最少被引用次数
                    maxAsyncRequests: 5,      
                    maxInitialRequests: 3,      
                    automaticNameDelimiter: '~',        // 自动命名连接符
                    cacheGroups: {        
                        vendors: {          
                            test: /[\\/]node_modules[\\/]/,          
                            minChunks:1,                        // 敲黑板
                            priority: -10                        // 优先级更高
                        },        
                        default: {          
                            test: /[\\/]src[\\/]js[\\/]/
                            minChunks: 2,                        // 一般为非第三方公共模块
                            priority: -20,          
                            reuseExistingChunk: true
                        }
                    },      
                    runtimeChunk:{         
                         name:'manifest'
                    }
                }
            } -->

解决问题

1、devServer启动时不编译,分别侦听

2、js文件引用动态生成到html文档内

3、第三方插件或公用组件splitChunk

| https://doc.webpack-china.org/concepts/ webpack中文网 v3.10.0
| https://www.jianshu.com/p/42e11515c10f
| http://webpack.wuhaolin.cn/ // book
| https://webpack.js.org/plugins/ // 自带插件
| https://zhuanlan.zhihu.com/p/32148338
| https://blog.csdn.net/keliyxyz/article/details/51571386
| http://react-china.org/t/webpack-output-filename-output-chunkfilename/2256/2 // output.filename 和output.chunkFilename
| http://www.alloyteam.com/2016/02/code-split-by-routes/ 按需加载
|
| 插件
| http://www.cnblogs.com/haogj/p/5160821.html // html-webpack-plugin html多页面构建
|
| 热更新
| https://github.com/gaearon/react-hot-loader // react
| https://github.com/vuejs/vue-loader // vue