没了脚手架,怎么用webpack!?

1. 什么是webpack?

webpack 是一个现代 JavaScript 应用程序的静态模块打包器(static module bundler),简单来讲,是一个前端模块化打包编译工具,基本的功能包括代码转换,文件优化,代码分割,自动刷新等。  
复制代码

2. 如何运用webpack

webpack配置文件是基于node.js,而且遵循common.js来开发,
一般咱们会在根目录下新建一个webpack.config.js文件,基本的结构目录以下图所示:
复制代码

2.1 配置

初始化:webpack3中,webpack和cli是在同一个包中,webpack4中已经分开。 yarn add webpack webpack-clicss

  • entry 入口
entry: {
    main: './src/index.js' //单入口
}
复制代码
entry: {
        main: './src/index.js', //多入口
        test: './src/test.js'
    }, //提示webpack把哪一个文件做为构建文件的入口
output: {
    path: path.resolve(__dirname,'dist'),//必须是绝对路径
    //filename: 'bundle.js'
    filename:'[name].[hash:8].js'//打包多个文件,各自名字,而且加上md5戳,8位
}, //构建结束输出的位置
复制代码

多入口打包出js。

打包后的js文件,名字bundle.js,若是想运行这个js,可是不能每次打包后都本身手动添加一个html,所以须要一个自动打包html的插件,而且可以自动把打包后的js添加上去。 第一个webpack插件 html-webpack-plugin 打包html做用 单入口html

let HtmlWebpackPlugin = rquire('html-webpack-plugin') //类
plugins: [
    new HtmlWebpackPlugin({
        fileName: 'index.html',//默认就是index.html
        template: './public/index.html',
        //以public下的index.html为模板打包
        minify: {
            removeAttributeQuotes: true, //去除引号
            removeEmptyAttributes: true //清除全部的空属性
            ...
        },
        hash: true, //html引js避免缓存
        ...
    })
]
复制代码

多入口自动打包引入js,利用循环前端

plugins: [
        new HtmlWebpackPlugin({
            template: './public/index.html',
            filename: 'index.html',
            minify: {
                removeAttributeQuotes: true,
                removeEmptyAttributes: true
            },
            chunks: ['main'],//指定入口文件名字,自动引入相应js文件
            hash: true
        }),
        new HtmlWebpackPlugin({
            template: './public/index.html',
            filename: 'test.html',
            chunks: ['test'],
            hash: true
        })
    ]
复制代码

html-webpack-plugin 参数连接vue

每次更改完代码,不能每次都手动打包,所以须要创建本地开发服务器,插件webpack-dev-server,而且它是在内存中打包,并不会看到dist目录,和build不一样;而且修改代码会实时刷新。java

devServer: {//开发服务器配置,这里单独列出,不在plugins内
        before(app){//若是不用转发,dev-server开启以前,前端本身mock数据
            app.get('/api/user',function(req,res){
                res.json({name:"fight"})
            })
        },
        contentBase: path.join(__dirname, 'dist'),//来自dist目录的文件提供服务,默认是output输出位置
        port: '3000',//更改端口号
        progress: true,//打包时候进度条
        compress:true //启动gzip压缩
        <!--proxy: {//node代理,假如页面访问api,node自动转发到3000端口-->
        <!--    '/api':{-->
        <!-- target: 'http://localhost:3000',--> <!-- pathRewrite: {--> <!-- '/api': ''//假如路径中有/api,重写置空--> <!-- } --> <!-- }--> <!--}--> }, 复制代码

2.1.1 编译css

loader有三种写法,字符串,数组和对象 当js须要样式的时候,import并不能解析,所以须要loader来转换模块node

  1. yarn add css-loader style-loader
module: {
        rules: [
            //css-loader 解析文中@import,style-loader 再以style方式插入样式
            //less 下载less less-loader,scss 下载node-sass sass-loader
            {
                test: /\.(css|less)$/,
                use: ['style-loader','css-loader','less-loader']//顺序不能变,不然报错
            }
        ]
    },
复制代码

2. 除了以style方式插入样式,还能够以Link方式插入样式,这里须要用到插件 mini-css-extract-plugin 假如css一些写法好比transform须要兼容其余浏览器,须要 postcss-loader 和插件 autoprefixer,而且根目录下新建postcss.config.js,内容以下

module.exports = {
    plugins: [require('autoprefixer')]
}

复制代码
let MiniCssExtractPlugin = require(mini-css-extract-plugin)
{   
    test: /\.(css|less)$/,
    use: [
        MiniCssExtractPlugin.loader, //抽取样式到Link标签中 mini-css-extract-plugin 
        {
            loader: 'css-loader',
            options: {}
        },
        'less-loader',
        'postcss-loader' //兼容前缀
    ]
}
plugins: [
    new MiniCssExtractPlugin({
        filename: 'main.css' //href名字,以下图所示
    })
    ] 

复制代码

2.1.2 编译js

目标:把ES6语法或者更高级别语法编译成ES5语法react

  1. @babel/preset-env 预设是插件的集合,能够在plugins里配置,或者新建.babelrc文件;
  2. 匹配全部js文件,用babel转化 @babel/core babel核心包, babel-loader,babel加载器 @babel/preset-env预设转化语法
  3. 高级语法须要其余插件补充 @babel/plugin-proposal-decorators@babel/plugin-proposal-class-properties, @babel/plugin-transform-runtime, @babel-polyfill, @babel-runtime
{   
    test: /\.js$/,
    exclude: 'node_modules',
    use: 
    [
        {
            loader: 'babel-loader',
            options: {
                presets: ['@babel/preset-env'],
                "plugins": [
                    ['@babel/plugin-proposal-decorators',{"legacy":true}],//解析装饰器
                    ['@babel/plugin-proposal-class-properties',{"loose":true}],
                    [ "@babel/plugin-transform-runtime"] //该插件依赖安装@babel/runtime,解析高级语法,好比yiled
                ]
            }
        },
        'eslint-loader' //根目录新建.eslintrc.json 配置代码规则 add eslint eslint-loader -D
    ],
    exclude: /node_modules/
}
复制代码

yarn add babel-polyfill 解析实例上的高级语法,在index中import进去 若是引入jquery,全局引用的话,暴露$,module中配置expose-loaderjquery

{
    test: require.resolve('jquery'),
    use: {
        loader: 'expose-loader?$',
    }
}

复制代码

2.1.3 编译图片

引入图片有三种方式: 1:new Image() 2.css background:url() 3.html中建立 yarn add file-loader html-withimg-loader -Dwebpack

module: {
        rules: [
            {
                test: /\.html$/,
                use: 'html-withimg-loader' //解决html中引入img不识别图片的问题
            },
            {
              test:/\.(png|jpg|gif)$/,
              use: 'file-loader' //会自动建立一个MD5戳的名字,而后把图片拷贝到打包后的dist 
            }
        ]
    }

复制代码

可是图片过多会引发资源请求过多,所以url-loader会优化这个问题 yarn add url-loader -Dgit

{
  test:/\.(png|jpg|gif)$/,
  //url-loader会调用file-loader,优化:避免过多发请求,把小的图片或icon转换成base64,体积变大,不适用大图片
  //use: 'file-loader' //会自动建立一个MD5戳的名字,而后把图片拷贝到dist 
  use: {
      loader: 'url-loader',
      options: {
        limit: 8*1024 //小于8k的图片会转换成base64,避免请求 
      }
  } 
}
复制代码

2.1.4 实时监控

output:{},
 watch:true,//实时监控
 watchOptions:{
    poll:1000,
    aggregateTimeout: 2000,//若是2秒内没动做,自动打包, 防抖
    ignored:/node_modules/
},
module:{}

复制代码

2.1.5 resolve

resolve: {
    modules: [path.resolve('node_modules')],//只查找当前路径的node_modules
    extensions: ['.js','.less','.json','.css'],//引入文件时省略扩展名
    alias: {//别名
        componets: './src/components/'
    }
}

复制代码

2.1.6 插件

1.每次打包后都会生成新的dist目录,clean-webpack-plugin 自动清空打包后的目录

let CleanWebpackPlugin = require('clean-webpack-plugin')
 plugins: [
    new CleanWebpackPlugin('./dist'),
]
复制代码
  1. 全局定义环境变量,webpack自带插件,js中PRODUCTION就是dev
let webpack = require('webpack')
plugins:[
    new Webpack.DefinePlugin({ //定义环境变量
        PRODUCTION:JSON.stringify('dev') // 定义PRODUCTION 是 dev环境
    })
    new Webpack.BannerPlugin('make by jeffywin'),//版权声明,自带插件
]
复制代码

2.1.7 webpack优化

当mode为production的时候,默认css是不压缩的,须要插件,直接在module.export中配置optimization

let UglifyJsPlugin = require('uglifyjs-webpack-plugin')
let OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')
optimization: {//优化
    minimizer: [//mode必须是production才有做用
        new UglifyJsPlugin({
            cache: true,
            parallel: true //并行,加速压缩
        }),
        new OptimizeCssAssetsPlugin({})//压缩css
    ]
}

复制代码

2.1.8 webpack优化 DllPlugin DLLReferencePlugin 动态连接库

在一般的打包过程当中,你所引用的好比bootstrap、react、react-router、antd、vue、vue-router、vuex 等等众多库每次开发打包都会打包进dist目录中,随着文件愈来愈大,打包的时间和代码体积也愈来愈大,因为这些库的内容基本不会发生改变,每次打包加入它们无疑是一种巨大的性能浪费。所以DllPlugin的出现就是解决这一问题,用某种方法实现了拆分 bundles,同时还大大提高了构建的速度。

  1. 额外建立一个js文件,DllPlugin插件会生成一个名为mainfest.json文件,这个文件是用来让DllReferencePlugin 映射到相关依赖上去
let path = require('path')
let webpack = require('webpack')
module.exports = {
    mode: 'development',
    entry: {
        react: ['react','react-dom',...] 
    },
    output: {
        filename: '_dll_[name].js', // 打包后的名字
        path: path.resolve(__dirname,'dist'),
        libraryTarget: 'commonjs2',
        libraryTarget: 'var',
        library: '_dll_[name]'//打包后会自动添加var _dll_[name] = {},来拿到内部的react,暴露出 (也叫作放入全局域) dll 函数
    },
    plugins:[
        new webpack.DllPlugin({//声明动态连接库
            name: '_dll_[name]', //暴露出的 DLL 的函数名
            path: path.resolve(__dirname,'dist','mainfest.json')//manifest json 文件的绝对路径 (输出文件)
        })
    ]
}
复制代码

运行 yarn run build -- --config webpack.dll.js,会生成两个文件

  1. DllReferencePlugin是在webpack主配置文件中设置
new ReferencePlugin({//开发优化 减小打包体积
        manifest:path.resolve(__dirname,'dist','mainfest.json') //html中还须要全局引用
    })
复制代码

manifest包含 content 和 name 的对象,或者在编译时(compilation)的一个用于加载的 JSON manifest 绝对路径,也就是引用DllPlugin打包后的文件

按需打包前

按需打包后
只是提早打包react和reactDom的状况下,打包文件大小和时间大大缩短,若是按需加载更多,效率会更高。

总结: webpack.dll.js:DllPlugin 打包生成打包后的文件 和 mainfest.json关系映射文件
webpack.config.js: 主配置文件DllReferencePlugin经过mainfest映射到json文件,json文件调用提早打包好的缓存文件

2.1.9 webpack优化 happypack

当项目比较大时,经过happypack插件,它将任务分配给多个子进程去并发执行,子进程处理完再发给主进程,从而减小构建时间,可是项目比较小时,不建议使用,子进程分配时间反而可能增长构建时间。

使用:经过把原来module中的配置移到plugins中

let Happypack = require('happypack')
module: {
    rules: [
        {
            test: /\.js$/,
            use: 'happypack/loader?id=js',
            exclude: /node_modules/,
            include: path.resolve('src')
        }
    ]
}
plugins: [
new Happypack(
    {
        id: "js",
        use:[{
            loader: 'babel-loader',
            options: {
                presets: ['@babel/preset-env', '@babel/preset-react'],
                "plugins": [
                    ['@babel/plugin-proposal-decorators', { "legacy": true }],//解析装饰器
                    ['@babel/plugin-proposal-class-properties', { "loose": true }],
                    ['@babel/plugin-transform-runtime'] //该插件依赖安装@babel/runtime,解析高级语法,好比yiled

                ] //es7高级语法,顺序不能变 
            }
        }],
        //'eslint-loader' //根目录新建.eslintrc.json 配置代码规则
        }
    )
]
复制代码

继续优化 yarn add webpack webpack-cli html-webpack-plugin @babel/core babel-loader @babel/preset-env @babel/preset-react

假如项目中引用了jquery等,能够用noParse来不去解析有关的依赖项 还有excludes,includes

module: {
        noParse: /jquery/,//不去解析jquery中的依赖项
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,//排除
                include: path.resolve(src),//包含
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: [
                            '@babel/preset-env',
                            '@babel/preset-react'
                        ]
                    }
                }
            }
        ]
    },
复制代码

假如引用了moment时间插件,会自动下载本地语言文件,致使打包文件过大,能够用webpack自带插件 webpack.IgnorePlugin

plugins: [
        //若是引入了monent,去除掉本地的语言插件,若是须要能够单独引用
        new webpack.IgnorePlugin(/\.\/local/,/moment/),
        new HtmlWebpackPlugin({
            filename: 'index.html',
            template: './public/index.html'
        })
    ]
复制代码

配置以后

多页面模块打包抽取公共代码和第三方库 假如同时引入了两个文件,或者jquery等第三方库,而且是多页面打包,能够将公共部分提早抽取出来打包

optimization: {
        splitChunks: {
            cacheGroups: {
                common:{
                    chunks: 'initial',
                    minSize: 0, //超过0个字节
                    minChunks: 2,//用过2次以上就抽离出来
                 },
                 venor: {//第三方
                    priority: 1,//权重,优先抽离
                    test: /node_modules/,
                    chunks: 'initial',
                    minSize: 0, //超过0个字节
                    minChunks: 2,//用过2次以上就抽离出来
                }
            }
        },
    },

复制代码

Webpack本质是一种事件流机制,工做流程就是将各个插件串连起来,实现这一的核心就是Tapable

相关文章
相关标签/搜索