美文网首页
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