webpack学习记录

212 阅读11分钟

前言

之前对webpack并没有过多的认识,只是跟着教程一步一步设置,再加上时间久了就完了,所以现在打算再复习一下,对其中基础常用的功能加深印象,顺便记录下来,当作自己以后翻阅的工具贴


webpack 是什么

目前一般项目都用webpack进行打包,由于前端各种模块化不通用,浏览器又不能识别那些语法,通过webpack可以进行转化,jsxts这些都可以,很方便,比如 react/vue 项目的脚手架,目前都是基于webpack来构建的

首先webpack 是依赖 node 环境的,所以必须要有 node,其次也暗示了其配置文件webpack.config.js的导入和导出应该使用Common JS的方式。


全局安装 webpack

简单试水一下,所以直接全局安装,然后直接针对某个文件夹里的文件进行打包,需要安装webpackwebpack-cliwebpack-cli是为了能在命令行里通过指令直接运行webpack)

因此直接npm install webpack webpack-cli -g ,然后就可以在一个文件夹里面试着运行了(具体是xx/src/xxx的文件目录形式,然后在xx下运行webpack,可以打包src里面的文件到xx/dist里)

比如,我在下面的文件里,index.js导入了js文件夹下的两个文件,分别是ES module和Common JS的方式,具体就不写出来了,知道意思就行:运行 node index 是不能正常运行的,会报错 SyntaxError: Cannot use import statement outside a module

image.png

文件目录如上所示,如果不是src下面的文件,直接命令行里面,敲webpack,运行全局的webpack,会报错

image.png

所以,虽然没有看源码,但是推断如果全局用的话,必须是要在src里写,然后默认打包src里的文件,输出到外面的dist文件夹下

image.png

然后运行webpack,就可以得到打包后的文件了,此处运行node main.js就能成功运行了。

image.png

局部安装 webpack

为什么有了全局还要安装局部的,因为多个项目可能依赖的不是同一个版本的webpack,全局的webpack只有一个版本,所以需要针对不同的项目安装不同版本的webpack。

在文件夹里,用npm init -y生成package.json后,有package.json的后就相当于是一个可以进行包管理的小项目了,就可以进行局部安装了,比如npm install webpack -D,这个时候要区分是生产环境还是开发环境


npm install -g/D/S

这里复习一下局部安装的 -S-D ; 参考

devDependencies与dependencies的区别,点击跳转参考链接

  1. devDependencies 里面的插件只用于开发环境,不用于生产环境,比如webpack
  2. dependencies 是需要发布到生产环境的(开发环境也得用,相对于全程都需要),比如vueantd

一开始我也分不清,觉得写在package.json里的开发环境和生产环境的依赖,只跟最后webpack打包有关系吗,node跟webpack最开始也没啥关系嘛,那一开始node搞的这个两种环境有啥作用哦?

在没有webpack的时候,package.json里面照样还是有区分devDependencies与dependencies,主要是两种情况:

  1. 发布到GitHub上的源码,其他开发者直接git clone下来,这个时候,是有着完整的package.json的,所以别人也是能完整的看到你的devDependencies与dependencies,当他们npm install 的时候,不管在哪上面的依赖都是会全部下载到node_modules下,就跟你自己开发的时候一模一样;
  2. 发布到npm上,别人就是通过npm install module_name的形式,下载你放上去的包,这个时候,只会下载你在dependencies里面的依赖
  3. 如果是在npm上下载你的项目,但是你写错了依赖,把vue写到devDependencies,那么别人安装的时候,就不会安装vue,就会提示错误,vue找不到;如果你把jest测试用的依赖写到dependencies里,别人下载安装就会把这个也安装上,别人的安装包就很臃肿,因为他们根本用不到jest

疑问,如下

  • 这两者的区别,在很多文章中都是说在项目打包/上线的时候,devDependencies的包不会打包进去,而一说到打包/上线我就很自然而然的想到webpack来打包一个web项目,然后部署上线,用户能用浏览器进行访问,然而其中的文章,又说像webpack之类的依赖,就不需要打包到生产环境里面,我就比较迷惑。

然后看到了这个回答(下面分割线),我想了一下,或许很多帖子也是互相借鉴,其中说的打包/上线,应该就是指的nodejs应用,也就是写了一个类似axiosvue这种项目,通过发布到npm上面,意思就是上线了这个项目,大家都可以去下载,这种项目可能也是经过webpack进行打包,只不过最后上线到npm上

然而如果说特指webpack来进行打包上线一个web项目,靠的webpack的逻辑,比如css-loader这些,安装在dev里面,而vue这些安装在dependence里面,通过webpack的逻辑判断哪些需要进行转换到es5,最后进行压缩打包,出来的就是一个包含了能完整运行项目逻辑的代码,不管是写在dependence还是devdependence里的依赖,只要相关的部分,都打包进来,这个“打包上线”到服务器似乎和刚刚那种项目打包上线到npm不太一样,打包环节通过webpack都是一样的打包,只不过最后一个是放到自己服务器上,比如直接就是能访问的网页了,不会再让其他开发者来接着用了,我这里称为项目,之前那个则是发到npm上,开发者可以继续下载,在此基础上用来开发自己的其他项目,我这里称为依赖。

说到底devDependencies与dependencies这俩的出现,是为了让开发者在通过npm install xxx的时候,只下载别人配置好的生产依赖就行,不必要把人家的开发依赖也下载,用不上,浪费空间。不要过多联想,我把自己都绕懵了。。。


image.png


有如下命令:(其中install可以直接简写成i

  • npm install 不带任何后缀的时候,表示本地安装,一般是项目初始化的时候,就直接在该项目文件夹下,把package.json里的module安装到./node_modules 下,通过require() 来引入。

  • npm install module_name -g global 的意思,全局安装,node和刚刚安装的webpack都是全局安装的,在哪都可以直接在命令行里用,module 的包放在 /usr/local 下或者你 node 的安装目录。

  • npm install module_name -S 或者 npm install module_name --save

-S 是 --save 的简写,表示安装到生产环境下,表明这个依赖从开发到上线(生产)都需要使用,会在dependencies里记录,不能小写-s

  • npm install module_name 不带后缀的时候,默认是--save,表示安装到生产环境下

  • npm install module_name -D 或者 npm install module_name --save-dev -D 是 --save-dev 的简写,表示安装到开发环境下,上线后不需要,因此不需要打包,会在devDependencies里记录,不能小写-d


回到上节内容

之后在命令行中执行npm webpack 依旧是用的全局的webpack,需要直接终端里执行./node_modules/.bin/webpack才是执行的局部安装的webpack,或者执行npx webpack (本地安装后执行) ,但是这样依旧不是很常用,npx 命令是用于执行node_module/.bin/目录下的shell脚本的命令

image.png

注意要安装webpack-cli

image.png

但是这样并不常用,所以一般是在package.json里的scripts里面配置webpack后再直接运行脚本,比如下图这样,然后直接运行npm run build

image.png

在命令行中执行 npm run build ,这样看起来比npx webpack还多写了一些单词,但是看截图里的 “test” 就知道,当命令很长的时候,就可以直接少写那一堆,用一个test代替


webpack 的配置

webpack的配置一般都很多,为了防止在package.json中写的script脚本过长,所以有一个文件来专门配置webpack,然后通过Common JS 的 module.exports = { } 导出该对象

webpack是通过node环境来运行的,所以它的配置文件应该用Common JS的语法来进行导出

配置文件的名字不能随便写,通常是webpack.config.js,而且这个导出了之后,不用自己去package.json里面导入啥的,webpack会自动在根目录里找到并读取,所以名字不能乱取

如果非要改名字,或者有不同的配置,比如生产环境和开发环境下,需要不同的脚本运行,就需要自己在package.json里单独配置了,具体如"build-custom":"webpack --config voiceu.js"

image.png

webpack.config.js如下:

const path = require('path')

module.exports = {
  entry: "./src/main.js",
  output: {
    // path必须是绝对路径,可以在命令行中输入 pwd 来获取,或者用 path模块
    path: path.resolve(__dirname, "./build"),
    filename: "bundle.js"
  }
}

在里面直接导出,在package.json里面只需要配置webpack的脚本就行,会自动识别这些配置,不需要再导入之类的


下面介绍一些常用的配置

entry

入口文件,默认是 index.js ,可以自己修改,比如上面的代码就改成了 main.js,好理解,不多说

output

输出文件,可选项有 pathfilename 等,看名字就知道啥意思,不过path需要是绝对路径,path必须是绝对路径,可以在命令行中输入 pwd 来获取,或者用 path模块来,在上面代码里都有写。

loader(重要)

名字就说清楚了,加载器,类似css这种,webpack并不能直接像识别js一样识别出来,需要一些loader来处理

一般有三种方式1、内联(比较少见);2、cli(webpack5就不再使用);3、配置文件

在配置文件里面是写在module对象里的rules数组里面,test正则匹配文件后缀,loader有顺序,use里面是倒序

module: {
    rules: [
      {
        test: /\.css$/, //正则表达式,表明文件后缀以.css结尾的就使用这条rule

        // loader 的语法糖
        // loader: "css-loader", //这是字符串

        // 完整写法,因为有可能是多个loader进行按序配合,所以是数组,顺序是倒序
        use: [
          // 简便写法直接写这个"css-loader"
          // "css-loader",
          // 完整写法,里面还可以有其他配置,这里没写出来
          // { loader: "css-loader" }

          // 注意是倒序,先执行最后的
          "style-loader",
          "css-loader",
          "postcss-loader"
          // {
          //   loader:"postcss-loader",
          //   options:{
          //     postcssOptions:{
          //       plugins:[
          //         require('autoprefixer')
          //       ]
          //     }
          //   }
          // }
        ]
      },
  }

常用的有

  • style-loader

把css写入style中,页面才能正常显示

  • css-loader

加载css

  • less-loader

需要搭配less,所以还要安装less

  • file-loader

webpack5用file-loader图片出错

  • url-loader 可转base64编码,同样会出现file-loader的问题,解决方法可见上方链接

  • vue-loader 用来加载.vue文件的

  • babel-loader babel可以有单独的配置文件babel.config.js 如果不在use下的options里面配置,就可以直接在配置文件里面写,

     {
        test:/\.js$/,
        use:[
          {
            loader:"babel-loader",
            options:{
              // plugins:[
              //   "@babel/plugin-transform-arrow-functions",
              //   "@babel/plugin-transform-block-scoping",
              // ]

              //可以通过babel.config.js来进行导入
              // presets:[
              //   "@babel/preset-env"
              // ]
            }
          }
        ]
      }
      

plugin(重要)

插件,做loader做不了的功能,很重要

在最上面,需要导入各个plugin的类,而且形式不一样,有的是解构,有的不是

// loader不用自己导入,直接在module里写就行,但是plugin需要手动导入
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require("html-webpack-plugin")
// DefinePlugin 是内置的plugin,不需要单独安装
const { DefinePlugin } = require('webpack')
const CopyWebpackPlugin = require("copy-webpack-plugin")
const { VueLoaderPlugin } = require("vue-loader/dist/index")


module.exports = {
  // plugins是数组,里面是对象,但是在引入的时候是类,所以需要创建新的对象
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      title: "voiceu",
      template: "./public/index.html",
    }),
    new DefinePlugin({
      BASE_URL: "'./'",
      __VUE_OPTIONS_API__: true,
      __VUE_PROD_DEVTOOLS__: false
    }),
    new CopyWebpackPlugin({
      patterns: [
        {
          from: "public",
          to: "./",
          globOptions: {
            ignore: [
              "**/index.html"
            ]
          }
        }
      ]
    }),
    new VueLoaderPlugin()
  ]
}

常用的有

  • CleanWebpackPlugin 每次打包之前,清空上次的打包文件

  • HtmlWebpackPlugin 生成一个html入口,可以指定模板

  • DefinePlugin webpack自带,不需额外下载,用于定义一些常量

  • CopyWebpackPlugin 把静态资源,复制到打包文件夹下,比如public下的favicon.icon

  • VueLoaderPlugin 用来加载.vue文件的


mode

  • mode: "development",
  • mode: "production",

devtool

默认是eval,教程说不利于查错,所以可以写成source-map,但实际上测试的时候发现eval才能映射到具体文件,可能版本原因

  • devtool: "eval",
  • devtool: "source-map",

watch

监听文件变化,只要发生了改变,就会自动重新编译,但是浏览器不会刷新,有两种方式

  • package.json里面对应的脚本后加上 --watch
  • 配置中,设置为true

devServer

安装 npm i webpack-dev-server -D 后,可以在package.json里面的脚本中添加"serve" : "webpack serve" 进行开启服务器,一般在开发阶段使用,这样就可以不用watch了,这个相对于也是通过写一个serve的配置选项,告诉终端运行某shell脚本,webpack会自动根据这个serve来选择运行bin下的webpack-dev-server,所以可以直接运行这个webpack-dev-server脚本,如在终端直接运行./node_modules/.bin/webpack-dev-server,也可以直接运行npm run serve

webpack-dev-server 也称 WDS,会通过express搭建服务器,浏览器也自动更新

Hot Module Replacement,HMR,用于热更新

image.png

  devServer: {
    // contentBase已经弃用了,改用static
    // contentBase:"./abc"

    // 因为在上面用了CopyWebpackPublic,所以这里会自动默认打包了public的内容
    static: {
      // directory: './static',
      directory: './public',
      // directory: path.join(__dirname, 'static/abc.js'),
    },
    hot: true,
    // host: "0.0.0.0",
    port: 7777,
    open: true,
    // compress: true,
    proxy: {
      "/api": {
        target: "http://localhost:8888",
        pathRewrite: {
          "^/api": ""
        },
        secure: false,
        changeOrigin: true
      }
    }
  },


resolve

用于模块解析,比如 require("./xxx"),其中xxx不用写后缀,就是通过这个,也可以设置别名,比如@

  resolve: {
    extensions: [".js", ".json", ".mjs", ".vue", ".ts", ".jsx", ".tsx"],
    alias: {
      "@": path.resolve(__dirname, "./src"),
      "js": path.resolve(__dirname, "./src/js")
    }
  },