webpack入坑之路

Hi ~ 许久没更新的小简书,自从换了新工作,日夜掉发式的疯狂加班,一个半月的时间学到了不少东西,趁热记录一波吧~


程序员病.png

一个月前,接到了一个需求,并且长期需要维护多个无关的活动页或者场景页,自以为简单的我乐呵呵的接了需求,心中已经想好“用react来搭一个多页面应用就够符合需求啦~”,事实证明,我还是too young too simple ,sometimes naive...

自拍照.png

实际开发工作中,react多页面应用的脚手架GitHub上有很多,自己也可以根据官方的react脚手架就可以愉快的撸起来,但是目前遇到的比较头疼的问题是,怎么让我的react应用代码压缩模块打包,这个时候就很有必要好好研究下webpack的使用了。

此次分享主要是针对多页面的webpack4打包。

So,咱们来聊一下,什么是webpack?

官网webpack图

官网上的webpack相信所有前端开发童鞋都见过,webpack做的事情,就是将我们的js应用的各个文件按照模块打包成多个bundle,是一个静态模块的打包器。

webpack做的事情,就是分析项目结构,找到js模块以及一些不能直接运行的语言,并将其打包成合适的格式给浏览器执行。

安装webpack

npm install webpack-cli 

了解webpack前需要知道webpack的四大核心概念,entry、output、loader、plugins。

Entry(入口)

entry是webpack的起点指示,告诉webpack从哪里开始入手打包任务,用来指定入口,默认值是./src。

如果是做单页面程序,单个入口。

//单个入口的写法  entry: string|Array<string>
entry:{
  main:'../src/component/my.js'
}
entry:'../src/component/my.js'
//如果传数组形式,是注入多个入口以及多个依赖文件。

如果是做多个页面,多个入口。

//多个入口的写法,所有的文件会被打包到dist文件中。
entry:{
  app:['./a.js','./b.js','./c.js']
}
//最终输出 dist/a.js   /  dist/b.js   dist/c.js

当webpack在解析代码时,每遇到import或者require引入的依赖,最终会被打包在最终构建结果中。打包的最终结果只会引入该模块引入的依赖。

output(输出)

打包完后输出的位置

output: {
    filename: 'vendor.js',
    path: '/home/index'
  }

loader(加载器)

实际上webpack只能打包js文件,其他资源例如css和html是需要加载器来将资源转化,加载进来。

举个栗子,如果项目中使用了sass的样式语法,是无法被浏览器所识别的,因此需要引用loader,将sass转义成能被识别的css语法阅读。

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

在该项目中,react用的是jsx的语法,同样无法被浏览器所识别,需要引用babel将jsx语法转义成正常的js运行。

module:{
  rules:[
     {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
          options: {
            presets: [
              "@babel/preset-env",//可以根据配置的目标浏览器或者运行环境来自动将2015+的代码转为es5
              "@babel/preset-react",
              { "plugins": ["@babel/plugin-proposal-class-properties"] }//这句可以在项目中使用箭头函数
            ],
          }
        },
  ]
}

loader的职责是单一的,一个loader相当于一个翻译官,只做翻译某种语言,需要关心输入和输出。
一个项目里有多个翻译官,在调用多个 Loader 去转换一个文件时,每个 Loader 会链式的顺序执行, 第一个 Loader 将会拿到需处理的原内容,上一个 Loader 处理后的结果会传给下一个接着处理,最后的 Loader 将处理后的最终结果返回给 Webpack。

plugins(插件)

常用的插件有

  • webpack-dev-server
  • copy-webpack-plugin
  • uglifyjs-webpack-plugin
  • webpack-spritesmith
  • clean-webpack-plugin
  • html-webpack-plugin
    ...还有好多好多

webpack-dev-server
非常常见,用于启动本地开发模式,本身是一个express服务器,封装了webpack-hot-middleware,实现了热更新。

devServer:{
  host:'0.0.0.0' || 'localhost',
  post:9000,//打开的端口
  open:true,//自动打开
  contentBase:path.join(_dirname,'src')//不设置的话,默认是当前执行的目录,一般是项目根目录,会在项目根目录查找index.html文件。
}

copy-webpack-plugin
在webpack相中拷贝文件或文件夹的方法。

new CopyWebpackPlugin({
  from:'.....',//需要拷贝的源目标文件
  to:'',//拷贝后存放的文件位置
})

uglifyjs-webpack-plugin
用来压缩优化js文件,webpack4+以上的版本可使用。
webpack4之前的版本是通过webpack.optimize.commonsChuckPlugin来压缩js

module.exports={
  optimization:{
    minimizer:true,//默认为true,效果就是压缩js代码
    splitChuncks:{
      chunks:'async',//'async':分割异步打包的代码,'all':同时分割同步和异步代码
      cacheGroups:{//默认的规则不会打包,需要单独定义
        vendors:{
          test:/[\\/]node_modules[\\/]/,
          name:'vendors'
        },
        commons:{
          test:/commons\.js/,
          name:'commons'
        }
      }
      
    }  
  }  
}

webpack-spritesmith
把零散的小图生成一张雪碧图,减少http请求

new SpritesmithPlugin({
      // 目标小图标
      src: {
        cwd: `src/assets/images/sprites`,
        glob: '*.png'
      },
      // 输出雪碧图文件及样式文件7
      target: {
        image: `dist/static/images/sprite-[hash].png`,
        css: [
          [
            `dist/static/css/sprite.css`, {
              formatOpts: {
                cssSelector: (groupName) => `.icon-${groupName.name}` // 修改生成的sprite css中类名定义
              }
            }
          ]
        ]
      },
      // 样式文件中调用雪碧图地址写法
      apiOptions: {
        cssImageRef: '../images/sprite-[hash].png'
      },
      spritesmithOptions: {
        algorithm: 'top-down'
      }
    })

clean-webpack-plugin
用来清除文件

// 删除文件 保留新文件
new CleanWebpackPlugin(['dist']),

html-webpack-plugin
为入口文件html加载js依赖,动态添加编译后的hash值文件,防止引用缓存文件的问题。

const HtmlWebpackPlugin = require('html-webpack-plugin')
  plugins: [
    new HtmlWebpackPlugin({ // 打包输出HTML
      minify: { // 压缩HTML文件
        removeComments: true, // 移除HTML中的注释
        collapseWhitespace: true, // 删除空白符与换行符
        minifyCSS: true// 压缩内联css
      },
      filename: 'a.html',
      template: 'a.html'
    }),
    new HtmlWebpackPlugin({ // 打包输出HTML
      minify: { // 压缩HTML文件
        removeComments: true, // 移除HTML中的注释
        collapseWhitespace: true, // 删除空白符与换行符
        minifyCSS: true// 压缩内联css
      },
      filename: 'b.html',
      template: 'b.html'
    }),
  ]

项目是多页的情况下,传入的html入口文件设置多个即可。
可以写一个提取html的函数,批量获取,组合成数组形式。

resolve配置

webpack在启动后会从配置的入口模块出发去找寻所有依赖的模块,resolve配置webpack如何找寻模块所对应的文件。
alias
resolve.alias配置项通过设置其他名字来将原路径映射成新的路径

resolve:{
  extensions: ['.js', '.json','.css'],//导入的语句中如果没带文件后缀名,webpack会自动带上后缀名去尝试文件是否存在,用于配置在尝试过程中用到的后缀列表。
  alias:{
    'api':utils.resolve('/src/common/api/'),//通过api关键字替换'/src/common/api/'
  }
}

总结

归结webpack在实际项目中做了哪些事情?
1、webpack从context设置的文件位置开始找寻
2、寻找entry里的所有文件名
3、在js文件编译过程中,遇到 import | require 引入的依赖文件,然后在依赖文件递归找寻所有依赖,最终打包在最终编译生成的文件。
4、webpack把所有生成的文件都输出到output.path路径中,以output.filename对应命名的模块来命名。

webpack之所以强大,是在于插件各式各样,总能满足你的需求。webpack4的新api大大的提高了Code Splitting 的体验,以上讲的内容还是浅显了点,具体的一些细节还是需要靠同志们自己愉快的撸起来,只有实操才是检验能力的唯一途径~


婊砸.png

最后,po上项目中的webpack相关依赖的版本情况

{
 "@babel/core": "^7.1.2",
    "@babel/plugin-proposal-class-properties": "^7.2.1",
    "@babel/plugin-transform-runtime": "^7.3.4",
    "@babel/preset-env": "^7.1.0",
    "@babel/preset-react": "^7.0.0",
    "@babel/runtime": "^7.3.4",
    "babel-loader": "^8.0.4",
    "babel-preset-env": "^1.7.0",
    "clean-webpack-plugin": "^2.0.0",
    "copy-webpack-plugin": "^5.0.3",
    "css-loader": "^1.0.0",
    "file-loader": "^2.0.0",
    "html-loader": "^0.5.5",
    "html-webpack-plugin": "^3.2.0",
    "image-webpack-loader": "^5.0.0",
    "jsx-loader": "^0.13.2",
    "postcss-loader": "^3.0.0",
    "sass-loader": "^7.1.0",
    "script-loader": "^0.7.2",
    "style-loader": "^0.23.0",
    "uglifyjs-webpack-plugin": "^2.0.1",
    "url-loader": "^1.1.2",
    "webpack": "^4.20.2",
    "webpack-cli": "^3.1.2",
    "webpack-dev-server": "^3.1.9",
    "webpack-spritesmith": "^0.5.4",
    "clean-webpack-plugin": "^2.0.0",
    "copy-webpack-plugin": "^5.0.3",
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容