美文网首页
Webpack全攻略

Webpack全攻略

作者: Jsonzhang | 来源:发表于2019-09-14 10:05 被阅读0次

    1. webpack和webpack-cli的安装

    cnpm install webpack webpack-cli -g
    // webpack -v 检查版本
    // webpack-cli -v
    

    全局安装有一个缺点,比如你有一个项目时用webpack4打包的,它依赖的一个项目使用webpack3打包的,这时如果你是全局安装的webpack4,就不能将项目运行起来。怎么办呢?
    卸载全局的webpack 和webpack-cli

    cnpm uninstall webpack webpack-cli -g
    

    然后我们只在我们的项目中安装需要的版本,进入项目目录,执行:

    cnpm install webpack webpack-cli -D
    //等价于
    cnpm install webpack webpack-cli --save-dev
    

    这时我们在项目目录下执行webpack -v是找不到这个命令,需要用npx webpack -v,这时才会显示版本号。
    那我们怎么安装指定的版本呢,以及怎么知道webpack都有哪些版本呢?

    cnpm info webpack
    
    image.png

    查到现有版本后,我们就可以按需安装了

    cnpm install webpack@3.12.0 webpack-cli -D
    

    这样我们就给项目安装了webpack3了

    2. 更改webpack默认配置文件

    webpack的默认配置文件是webpack.config.js,那么如果我们自定义一个文件叫webpack.js,想让它作为配置文件,怎么办呢?npx webpack --config webpack.js执行这个命令可以改变。

    3. 更改默认的命令方式

    我们在打包时用的默认命令是npx webpack,我们还可以自定义命令,比如在package.json文件中scripts标签里配置:

    "scripts": {
        "build": "webpack"
    }
    

    这样我们再运行bundle命令,就会帮我们打包了,命令是npm run build

    4. 一个简单的webpack.config.js如下

    const path = require('path');
    module.exports = {
        mode:'production', //development
        entry:'./src/index.js',
        output:{
            filename:'main.js',
            path:path.resolve(__dirname,'dist');
        }
    }
    //production 环境输出的是压缩的代码,而development 不压缩
    

    5. 文件打包

    我在index.js文件引入一个图片import logo from '.src/logo.png',这是我们再打包,发现报错,因为webpack默认只能打包js文件,其他的文件需要其他的loader来处理。
    所以我们安装file-loadercnpm install file-loader -D,然后在webpack.config.js中配置。

    const path = require('path');
    module.exports = {
        mode:'development', //production
        entry:'./src/index.js',
        output:{
            filename:'main.js',
            path:path.resolve(__dirname,'dist');
        },
        module:{
          rules:[{
            test:/\.png$/,
            use:{
              loader:'file-loader'
            }
          }]
        }
    }
    

    6.怎样让打包出的文件名不变

    比如我们打包一个logo.png的图片,然后在dist目录下,你发现图片的名字是一长串的字符,那怎样让图片的名字不变呢,我们可以继续在webpack.config.js中配置options。

    const path = require('path');
    module.exports = {
        mode:'development', //production
        entry:'./src/index.js',
        output:{
            filename:'main.js',
            path:path.resolve(__dirname,'dist');
        },
        module:{
          rules:[{
            test:/\.png$/, //   /\.(png|jpg|gif)$/  多重选择
            use:{
              loader:'file-loader',
              options:{
                  name:'[name].[ext]'
              }
            }
          }]
        }
    }
    

    7. 怎样让打包出的图片在一个单独文件夹

    比如想让打包出的图片在dist/images/目录下,怎么配置呢?

    const path = require('path');
    module.exports = {
        mode:'development', //production
        entry:'./src/index.js',
        output:{
            filename:'main.js',
            path:path.resolve(__dirname,'dist');
        },
        module:{
          rules:[{
            test:/\.png$/, //   /\.(png|jpg|gif)$/  多重选择
            use:{
              loader:'file-loader',
              options:{
                  name:'[name].[ext]',
                  outputPath:'/images'
              }
            }
          }]
        }
    }
    

    8. 认识url-loader

    首先要知道url-loader和file-loader有相同的功能,它也能打包文件。但是它打包的文件是以base64的形式存在js文件中的,这种方式有优点也有缺点:
    优点:
    如果文件比较小,比如一张图片5kb,打包后这个图片以base64的形式存在js中,就会加载js文件时候,一起把图片加载进来了,省去一次单独的请求。整体加载速度更快了。
    缺点:
    如果一个文件比较大,那么打包后还是以base64的形式存在js中,就会导致这个js文件比较大,直接导致加载js速度比较慢。

    所以如果有一个配置项,比如文件的临界大小是10240字节(10kb),大于这个值我们单独打包出来放到一个文件目录中,比如/images。如果文件大小小于这个值,就直接打包到js文件中,是不是就完美了。

    const path = require('path');
    module.exports = {
        mode:'development', //production
        entry:'./src/index.js',
        output:{
            filename:'main.js',
            path:path.resolve(__dirname,'dist');
        },
        module:{
          rules:[{
            test:/\.png$/, //   /\.(png|jpg|gif)$/  多重选择
            use:{
              loader:'url-loader',
              options:{
                  name:'[name].[ext]',
                  outputPath:'/images',
                  limit:10240 //大于单独打包到文件,小于以base64打包到js文件
              }
            }
          }]
        }
    }
    

    别忘了前提安装url-loadercnpm install url-loader -D

    9. 打包css文件

    打包css文件,就需要用到style-loader和css-loader
    cnpm install style-loader css-loader -D
    然后在rules里面加入

    {
        test:/\.css$/,
        use:['style-loader','css-loader']
    }
    

    10. 打包scss文件

    打包scss文件需要用到node-sass和sass-loader
    cnpm install node-sass sass-loader -D
    然后在rules里面加入

    {
        test:/\.scss$/,
        use:['style-loader','css-loader','sass-loader']
    }
    

    注意:如果用的多个loader,执行顺序从下到上,从右到左,比如上面的就是先用sass-loader将scss文件解析成css文件,然后用css-loader打包css文件,最后用style-loader将样式挂载到head标签的style标签中。

    11.认识postcss-loader

    我们在写一些样式的时候要考虑兼容性问题,比如
    transform:translate(100px,100px)
    这些样式一般都要加厂商前缀来兼容,但是自己写又比较麻烦,有没有一个工具可以帮助我们写,这里就用到postcss-loader,首先还是安装:
    cnpm install postcss-loader -D
    然后需要新建一个postcss.config.js文件,里面写一些配置:

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

    这里需要用到一个插件:
    cnpm install autoprefixer -D

    12. css-loader一些其他配置

    {
        test:/\.scss$/,
        use:['style-loader','css-loader','sass-loader','postcss-loader']
    }
    

    上面是我们打包的scss文件的配置,打包顺序是postcss-loader先加前缀,sass-loader解析成css,css合并,然后挂载到head中的style标签。
    但是如果我一个scss文件中,上面依赖其他的scss文件,比如这样:

    @import './a.scss'
    body{
      #root{
          width:100px;
          height:100px;
          background:red;
       }
    }
    

    如果像上面那样写配置,a.scss文件可能就不会被先post,然后在sass,所以为了让a.scss也走相同的解析顺序。我们要这样配置:

    {
      test:/\.scss$/,
      use:[
          'style-loader',
          {
              loader:'css-loader',
              options:{
                  importLoaders:2
              }
          },
          'sass-loader',
          'postcss-loader'
       ]
    }
    

    还有一个配置,比如一个js文件导入了一个scss文件@important './index.scss',这个js文件的代码就是创建一个div挂载到body上,这个js文件还引用了另一个js文件,里面的代码也是创建一个div。并通过img.classList.add()的方式添加样式,上面的导入方式是全局导入的方式,改变scss中的代码可能会改变很多文件的样式。那么我们想让某个scss文件只作用于某个文件怎么做呢?
    首先导入方式更改为:@important style from './index.scss';
    然后需要添加css模块化属性:

    {
        loader:'css-loader',
        options:{
            importLoaders:2,
            modules:true  //css模块化
        }
    }
    

    13. 怎样打包图标字体文件

    从iconfont网站找到自己想要的图标,下载下来,把.eot .svg .ttf .woff的字体文件放到font文件夹下,把iconfont.css中的代码拷贝到自己的样式代码中。
    <div class='iconfont iconfont-market'></div>
    这样就会显示图标了
    配置中这样配置:

    {
        test:/\.(eot|svg|ttf|woff)$/,
        use:{loader:'file-loader'}
    }
    

    14.认识Html-Webpack-Plugin

    安装cnpm install html-webpack-plugin -D
    作用:HtmlWebpackPlugin会在打包结束后,自动生成一个html文件,并把打包生成的js自动引入到这个html文件中。
    进行配置:

    const HtmlWebpackPlugin = require('html-webpack-plugin'); //首先要引入
    
    plugins:[
      new HtmlWebpackPlugin()
    ]
    

    怎样让生成的html文件有固定的元素呢,那就需要一个模板,我们可以在src下新建我们的模板template.html文件,然后做如下配置:

    const HtmlWebpackPlugin = require('html-webpack-plugin'); //首先要引入
    
    plugins:[
      new HtmlWebpackPlugin({
        template:'./src/template.html'
      })
    ]
    

    这样打包生成的html文件就定义的模板html一样了。

    15.认识CleanWebpackPlugin

    安装:cnpm install clean-webpack-plugin
    作用:每次执行打包命令后,都会先将原来打包的dist删除,它不是webpack官方的plugin。

    const { CleanWebpackPlugin } = require('clean-webpack-plugin');
    plugins: [
            new CleanWebpackPlugin({
                try: false,
                watch: false
            }),
        ]
    

    16. 同一个入口文件,生成不同的文件名

    entry:{
        main:'./src/index.js',
        sub:'./src/index.js'
    }
    
    output:{
        publicPath:'http://cdn.com.cn', //注入打包后的html中的script标签前缀
        filename:'[name].js',
        path:path.resolve(__dirname,'dist')
    }
    

    17.source-map的作用

    module.exports = {
        mode:'development', //开发模式下是打开了devtool:source-map的
        devtool:'none', 
        entry:{},
        output:{}
    }
    

    当设置devtool:'none'时,如果js中的代码有问题,在控制台显示的位置是在打包后的文件中的,这样定位错误非常不方便,如果我们想让错误定位在原始的js代码中,就需要设置devtool:'source-map'
    devtool有很多值可以选,建议开发中使用devtool:'cheap-module-eval-source-map'
    这种方式打包速度快,错误提示全。
    如果是线上环境建议使用:devtool:'cheap-module-source-map'

    18. WebpackDevServer提升开发效率

    每次打包都要运行npm run build,然后在到dist目录下打开index.html才能看到改动的效果,过程有点麻烦。那有没有方法,可以监听我的源代码改变,我的源代码一改变,就自动打包,然后我们刷新页面就可以看到效果了。方法有2种:
    第一种:改变package.json中的配置:

    "scripts": {
       "build": "webpack --watch"
     }
    

    第二种:devserver可以感知我们的源代码改变,并自动打开刷新浏览器
    安装:cnpm install webpack-dev-server -D
    package.json中加入运行命令:

    "scripts": {
       "build": "webpack --watch",
        "devserver":"webpack-dev-server"
     }
    

    webpack.config.js配置:

    module.exports = {
        mode:'development', //开发模式下是打开了devtool:source-map的
        entry:{},
        output:{},
        devServer:{
            contentBase:'./dist',
            open:true
        }
    }
    

    用devserver解决开发阶段的跨域问题:

     devServer:{
         contentBase:'./dist',
         open:true,
         proxy:{
            '/api':'http://zhangsan.com'
         }
     }
    

    19. 认识HotModuleReplacement 热模块更新

    image.png
    需求场景:
    有一个按钮点击后页面就增加一个div元素,第偶数个元素的背景是有颜色的。这时我把背景色改成蓝色,想看下是否起作用了。由于页面刷新后,现在页面只有一个增加按钮,我需要再点击增加出许多元素,才能发现我改的颜色是否对的。
    我的理想状态是页面元素个数不变,只有颜色做更新,也就是我需要的效果更新,其他的不更新。怎么办呢?
    这就是要说的热模块更新
    首页在webpack.config.js中导入webpack,因为我们一会要用到webpack的一个插件。
    cont webpack = require('webpack');
    然后在devServer中开启热模块更新:
    devServer:{
         contentBase:'./dist',
         open:true,
         proxy:{
            '/api':'http://zhangsan.com'
         },
        hot:true,
        hotOnly:true //可加可不加,加表示热更新可能失效,但是此时浏览器也会帮你更新下,这是hotOnly就是说如果失效就失效,也别刷新。
     }
    

    然后添加热模块更新的插件:

    plugins:[
        new webpack.HotModuleReplacementPlugin()
    ] 
    

    这时我们只对样式文件做更改就可以了。

    还有一种情况,比如一个a.js文件导入了b.js 和 c.js文件,b的js代码就是展示一个数,点击自增1。而c的js就展示一个数字。


    image.png

    这时我点击11,数字开始增大,比如增加到19。然后我去c.js把200改成300,你会发现页面整体刷新,上面数组19回到原始值11。
    这就是热更新中的问题,我只想更新下面的数字,不想刷新上面的数字,怎么办呢?
    首先上面的热更新配置到配置好后,需要在a.js写如下代码:

    import number1 from './b.js'; //里面是一个方法,然后export default 导出
    import number2 from './c.js';
    
    number1(); //显示数组11
    number2(); //显示数字200
    
    if(module.hot){
        module.hot.accept('./c.js',()=>{
            //处理一些其他逻辑
            //更新
            number2();
        });
    }
    

    20. Webpack中用babel处理es6语法

    cnpm install babel-loader @babel/core -D
    rules中加入下面规则:

    {
      test:/\.js$/,
      exclude:/node_modules/,
      loader:'babel-loader'
    }
    

    然后在安装:
    cnpm install @babel/preset-env -D
    babel-loader只是webpack和babel一个桥梁,有了它他们就算认识了,但真正处理es6语法的是babel/preset-env,它里面涵盖了转化es6的一些规则。
    然后我们需要再配置:

    {
      test:/\.js$/,
      exclude:/node_modules/,
      loader:'babel-loader',
      options:{
        presets:['@babel/preset-env']
      }
    }
    

    这样配置之后,一些es6语法可以被转换成es5,比如const转成var,箭头函数转换成普通方法。但是比如new Promise()有些浏览器就没有,我们怎么用es6呢,我们需要用到babel-polyfill来补充这些缺失。
    cnpm install @babel/polyfill -D
    然后在每个用到es6的js文件顶部引入:import '@babel-polyfill;'
    但是这样引入后,打包后你会发现main.js的大小变大了很多,因为babel-polyfill实现了es6中缺失的东西,比如promise,比如arr.map()函数,他会用代码实现一遍。那这样全部实现导致main.js变大,有没有一个方法,让babel-polyfill只实现我文件用到的es6语法,没用到的就不用实现,也就是按需实现,就会大大减小main.js文件的大小。
    没错,这是可以实现的,配置如下:

    {
      test:/\.js$/,
      exclude:/node_modules/,
      loader:'babel-loader',
      options:{
        presets:[['@babel/preset-env',{
            useBuiltIns:'usage'
        }]]
      }
    }
    

    babel/preset-env还有其他许多设置,比如target属性:

    {
      test:/\.js$/,
      exclude:/node_modules/,
      loader:'babel-loader',
      options:{
        presets:[['@babel/preset-env',{
            useBuiltIns:'usage',
            targets:{
                chrome:'67'
            }
        }]]
      }
    }
    

    这表示我运行的环境是chrome 67的版本,webpack会判断chrome 67已经完全兼容es6语法了,就不会做任何处理了。

    有时options里面的内容非常多,我们可以把options的内容单独拿出了,放到一个文件中,我们新建一个文件叫 .babelrc,然后我们把options的内容搬过来:

    {
        presets:[['@babel/preset-env',{
            useBuiltIns:'usage',
            targets:{
                chrome:'67'
            }
    }
    

    然后webpack.config.js文件里面的内容就剩这样了:

    {
      test:/\.js$/,
      exclude:/node_modules/,
      loader:'babel-loader'
    }
    

    相关文章

      网友评论

          本文标题:Webpack全攻略

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