美文网首页
webpack4伪入门手册

webpack4伪入门手册

作者: huk | 来源:发表于2019-03-26 16:31 被阅读0次

    写在最前面,这算是自己学习、实践后的记录,不是篇详细的指南,每个点挑了重点的地方说(平时开发中真接触到了的地方),每个详细的点儿可以去webpack官方文档看具体的内容,文档挺清晰的。

    基础篇

    基础配置

    首先安装依赖 npm install webpack webpack-cli -D

    webpack4可以0配置打包 直接运行 npx webpack 会到bin下面去找webpack 然后进行默认配置的打包

    用来打包其实就是支持js的模块化

    默认配置文件命名 webpack.config.js和webpackfile.js,手动指定配置文件的时候要—config xxxx

    模式(mode)有两种production和development,默认是生产,production会压缩文件进行优化,development 开发环境,可以看到打包后的结果,不会进行压缩和优化的操作

    问:webpack打包后的结果为什么可以在浏览器中执行?

    //基础配置
    module.exports = {
        entry:'./src/index.js',
        mode:'development',
        output:{
            //有更改的时候会hash修改
            filename:'bundle.[hash].js',
            path:path.resolve('dist') //path必须是一个绝对路径
        }
    }
    

    文件解析

    分析一个最简单的打包出来的bundle文件

    webpack内部实现了一个require的方法,实现了递归的依赖关系。从入口文件开始加载模块(加载的时候会存进缓存,如果缓存中有了就不install这个模块),加载模块后,会call执行这个模块(key:value的形式组合的 modules[moduleId].call(…) moduleId就是key),然后找到了这个模块里还引入别的模块,就继续require,exports结果。

    HTML plugin

    配置devserver开发服务器

    module.exports = {
        entry:'./src/index.js',
        //还可以配置压缩、open、地址等等
        devServer:{
            port:3000,
            contentBase:'./dist'
        },
        mode:'development',
        output:{
            filename:'bundle.[hash].js',
            path:path.resolve('dist') //path必须是一个绝对路径
        }
    }
    

    webpackHtmlPlugin

    在我们的项目中添加一个index.html作为模板,把打包出来的文件放进这个html中展示

    const HtmlWebpackPlugin = require('html-webpack-plugin');
    module.exports = {
        //...,
        plugins:[
            new HtmlWebpackPlugin({
                template:'./src/index.html',
                filename:'index.html',
                //可以配置一些压缩 比如去掉双引号 折叠空行
                minify:{
                    collapseWhitespace:true
                },
                hash:true
            })
        ]
    }
    

    样式处理

    使用各种loader去处理对应的文件类型

    css-loader 处理css文件 主要负责解析@import这种语法

    style-loader 将解析好的css放进style标签里插入到html中

    常见预处理器:

    • less less-loader

    • node-sass sass-loader

    • stylus stylus-loader

    自动添加浏览器前缀:

    postcss-loader 配置autoprefixer

    抽离css样式:

    mini-css-extract-plugin

    抽离后的css可以利用optimize-css-assets-webpack-plugin来压缩 配置在webpack的优化项中

    module.exports = {
        //...,
        optimization:{
            minimizer:{
                //要注意如果minimizer修改了之后要把js压缩手动添加上去 不然js压缩会失效
                new UglifyJsPlugin({
                    ...
                }),
                new OptimizeCss()
            }
        }
    }
    

    转化es6语法 babel 处理js语法

    使用babel转化语法 首先安装

    babel-loader 处理的loader

    @babel/core 这个是babel的核心模块

    @babel/preset-env 这是个转化模块,可以把一些高级的语法转化成低级的语法

    module.exports = {
        //...,
        module:{
            rules:[
                {
                    test:'/\.js$/',
                    exclude:'/node_module/',
                    use:{
                        loader:'babel-loader',
                        options:{
                            //所有预设
                            presets:[
                                '@babel/preset-env'
                            ],
                            //很多更加高级的语法预设中没有 就要自己添加 比如class 装饰器 
                            plugins:[
                                "@babel/plugin-proposal-class-properties",
                                //预设可以转化代码但是不能加上es6内置的方法 
                                //所以要加入这个插件来 对应上内置的方法。
                                //但是有一个不能转换include include是实例上的 要用polyfill
                                "@babel/plugin-transform-runtime",
                              
                            ]
                        }
                    }
                }
            ]
        }
    }
    

    全局变量引入问题

    比如我们引入jquery 直接import $ from 'jquery' 可以打出$的值,但是window.$ 不能打出来,没挂在window上。想要挂上去可以

    1.expose-loader

    内联loader

    暴露到window上(在引入的页面或者webpack的loader里面加内联loader都可以)

    //方法一:内联loader
    import $ from "expose-loader?$!jquery"
    console.log(window.$)
    //方法二:webpack config中配置 但是页面里还是要import $ from "jquery"的
    module.exports = {
        //...,
        module:{
            rules:[
                {
                    //代码中引用了jq
                    test:require.resolve('jquery'),
                    use:'expose-loader?$'
                }
            ]
        }
    }
    

    2..ProvidePlugin

    不想在组件里引入jq,那就用这个方法在每个模块中注入$ 就可以直接用$不需要引入了

    module.exports = {
        plugins:[
            new webpack.ProvidePlugin({
                $:'jquery'
            })
        ]
    }
    

    3.引入cdn 且external掉jq

    运用的地方用了import进来 不用的话其实就不会被打包进来,cdn的时候其实已经引入了 重新import一下等于又引入一遍

    用external可以让引入的包不被打包

    图片处理

    图片的引用有三种 1.js中 2.css中 3.html中

    1.通过配置file-loader来读取图片

    //js
    import logo from "./logo.png"; //引入获取的是新的图片地址(hash的)
    let image = new Image();
    image.src = logo;
    
    //config
    {
        test:/\.(png|jpg|jpeg|gif)$/,
        use:'file-loader'
    }
    

    2.css中是可以直接使用的,因为css-loader处理了这个问题,会去把括号里的路径引入进来

    伪代码:background:url('../img/logo.png') -> background:url(require('../img/logo.png'))

    3.使用html-withing-loader来转化html中图片的路径

    {
        test:/\.html$/,
        use:'html-withing-loader'
    }
    

    但是在我们实际开发过程中,很多图片都比较小,其实是不希望这些小图也发个http请求的,所以就采用一定大小内使用base64(但是base64的大小会比原来的图片大大约三分一),超过大小发请求的方式,使用url-loader可以实现。

    {
        test:/\.(png|jpg|jpeg|gif)$/,
        use:{
            loader:'url-loader',
            options:{
                limit:8192    
            }
        }
    }
    

    打包文件分类

    可以在不同的output的时候加上文件夹路径

    //比如url-loader里
    {
        test:/\.(png|jpg|jpeg|gif)$/,
        use:{
            loader:'url-loader',
            options:{
                limit:8192,
                name: 'images/[hash:8].[name].[ext]'
            }
        }
    }
    //minicss中也是 filename:'css/[name].css'之类
    

    如果要统一在打包路径前面加内容,比如cdn,比如之前项目中使用dll打出来的前面少' ./ ',可以在output中配置publicPath,打包出来文件引用的时候就会前面加上这个publicPath了(同理,如果不想全部引用都加的话,可以在想要加的地方配置publicPath,比如图片就在url-loader中配置)

    配置篇

    打包多页面应用

    多个entry多个chunk,对应多个html

    {
        entry:{
            entry1:path.resolve('./src/entry1.js'),
            entry2:path.resolve('./src/entry2.js'),
        },
        ...
        plugins:[
            new HtmlWebpackPlugin({
                template:'index.html',
                title:'111',
                chunks:['entry1'],
                filename:'entry1.html'
            }),
            new HtmlWebpackPlugin({
                template:'index.html',
                title:'222',
                chunks:['entry2'],
                filename:'entry2.html'
            })
        ]
    }
    

    配置devtool

    对source-map的配置

    eval:提高构建效率 (映射到构建后的代码)

    cheap:sourcemap没有列信息,大幅提高 sourcemap 生成的效率

    module:简化loader的sourcemap(不包含列信息)

    watch(check一下)

    监控代码的变化,实时打包

    {
        watch:true,
        watchOptions:{
            poll:1000, //每秒监控1000次
            aggregateTimeout:500 //防抖 一直输入的话不会触发 停止后过了500ms才触发
            ignored: /node_modules/
        }
    }
    

    常用小插件

    1.clean-webpack-plugin clean文件夹

    2.copy-webpack-plugin 拷贝文件

    3.webpack.bannerPlugin 为每个 chunk 文件头部添加 banner

    跨域问题解决

    1.devserver中配置proxy代理

    配置target,/api开头的都去 http://localhost:4000

    devServer: {
            port: 3000,
            contentBase: './dist',
            proxy:{
                '/api':{
                    target:'http://localhost:4000'
                }
            }
        }
    

    2.如果只是想要前端这边自己mock点数据,可以使用devserver的before方法

    devServer:{
        //app就是express实例化的
        before(app){
            app.get('/user',(req,res)=>{
                res.json({name:'huk'})
            })
        }
    }
    

    3.不用代理来处理,在服务端启动webpack,并且端口用服务端的端口

    服务器端启动webpack 使得前后端启动在一个端口上 express加上中间件 启动webpack devserver(webpack的compile传进sever 在middlerware中启动)

    let express = require('express');
    let app = express();
    let webpack = require('webpack');
    let middle = require('webpack-dev-middleware');
    let config  = require('./webpack.config');
    let compiler = webpack(config);
    
    app.use(middle(compiler))
    app.get('/api/user',(req,res)=>{
        res.json({name:'hahaha'})
    })
    app.listen(4000)
    

    resolve配置

    {
        resolve:{
            //从哪里开始依赖 避免一直往上查
            modules:[path.resolve('node_modules')],
            //别名 比如import bootstrap的时候,其实是引入的bootstrap.js,这又要依赖jq
            //这样别名可以直接引入某个文件
            alias:{
                bootstrap:'bootstrap/dist/css/bootstrap.css'        
            },
            //自定义扩展名的优先级 默认不写扩展名是读的.js 这边配了之后会往后找
            extensions:['.js','.css','.json']
        }
    }
    

    环境变量定义

    webpack.DefinePlugin

     new webpack.DefinePlugin({
         'process.env.api':JSON.stringify(process.env.api)
     })
    

    webpack.EnvironmentPlugin

    用起来比较方便,把上面DefinePlugin的写法简化了

    区分不同环境

    利用webpack-merge配置多个config文件。比如,dev和prod环境下有不同的配置,就把公共地方的写一份webpack.base.config.js,在dev和prod config中merge base,然后加上这个环境下独有的配置

    • webpack.base.config.js
    • webpack.dev.config.js
    • webpack.prod.config.js

    优化

    列举下可以考虑的点儿,用法不赘述了,可以去查对应的文章。这块准备重新写一篇,太多了。

    • happypack 多线程打包loader

    • dll 打包三方依赖

      项目有业务代码和三方库,三方库比较稳定,但是在构建的时候也会重新构建,这样减慢了构建速度。使用dll可以把常用的三方库放进动态链接库中,构建时候不需要重新打包。

      DllPlugin+DllReferencePlugin dll打包的操作是在项目打包之前做的,然后可以用html-webpack-include-assets-plugin把打包出来的dll插入到想要插入的html中

      或者可以使用autodll plugin,这插件可以把上面的步骤融合进来,不用自己去配了,但是在多页面的情况下比较局限性,比如想打两个dll文件,一个每个打出来的html都用,另一个只给某一个html用,这就不行了,需要自己改下autodll的源码,让inject支持匹配。

    • webpack自带优化项

      配置optimization

    • IgnorePlugin

      可以指定某个东西不打包进去 比如moment打包进来的时候 里面有个local的语言包 我们不需要那么多的语言包 所以可以打包时忽略掉这个文件夹 (如果要引入别的语言包可以在项目内单独import里面的某个语言包然后设置语言)

    • noParse 不去解析某个包中的依赖库

    • 热更新

    最后

    写的不对的地方希望指正,也欢迎交流,谢谢大佬们0-0

    相关文章

      网友评论

          本文标题:webpack4伪入门手册

          本文链接:https://www.haomeiwen.com/subject/aoqnvqtx.html