1. 首页

深夜被女友手把手入门webpack的感觉

深夜被女友手把手入门webpack的感觉

深夜被女友手把手入门webpack的感觉

为什么要构建?

前端发展好快的。

越来越多的思想和框架都出来了。

就是为了提高开发的效率。

比如ES6需要通过babel转成ES5才能在浏览器上面运行吧。

比如SCSS需要转换成css才能在浏览器运行吧

这些思想和框架,都是有构建需求的。

为什么要选择webpack来做构建?

webpack 把一切都当成模块!

webpack可以通过plugin来拓展功能!

webpack的社区非常的庞大活跃,紧跟着时代!生态链完整,维护性也很高!

想要理解为什么要使用 webpack,我们先回顾下历史,在打包工具出现之前,我们是如何在 web 中使用 JavaScript 的。 在浏览器中运行 JavaScript 有两种方法。第一种方式,引用一些脚本来存放每个功能;此解决方案很难扩展,因为加载太多脚本会导致网络瓶颈。第二种方式,使用一个包含所有项目代码的大型 .js 文件,但是这会导致作用域、文件大小、可读性和可维护性方面的问题。

为什么选择 webpack : webpack.docschina.org/concepts/wh…

webpack初次打包

webpack是一个模块打包工具,但是只能打包js,所以如果要打包其他的比如css,图片之类的模块,还需要借助loader,loader不能够解决的问题还需要借助插件plugin来拓展功能。

安装

yarn add webpack webpack-cli

配置

  1. 创建webpack.config.js文件
  2. 配置webpack.config.js

const path = require('path') module.exports={ entry: { main:'./src/index.js' }, output: { path: path.resolve(__dirname, 'dist'), filename: "[name].js" } } 复制代码
  1. 配置packge.json

"script":{ "start":"webpack" } 复制代码

解释配置

其实webpack里面已经默认配置了很多非常丰富的东西,比如output,如果我们不配置output的话,默认就会输出一个dist文件夹,里面有着名叫bundle.js的文件。

我们也可以自己配置:

  1. entry

    入口文件的配置,如果只有一个入口文件,我们可以entry:'./src/index.js'

    如果有多个入口文件,或者你想要给你的入口文件指定一个名字:里面的main就是入口文件的名字。

    这个入口文件的名字有一个好处就是,后面很多配置可能都会用到:我们不需要再手动输入main, 仅仅是使用'[name]'就可以锁定到之前配置的main

    这样就可以实现,改一处,便可改所有。

  2. output

    path是你输出的时候,会生成一个文件夹,文件夹里面有一个文件filename,文件的名字叫做'[name].js'。我们上面提到的,这个[name]就对应着入口文件名!

loader

因为webpack只能够打包解析js代码,所以面对非js的模块,我们要用loader来解析!

css

安装

yarn add style-loader css-loader

配置


module.exports={ module: { rules: [ { test:/\.css$/, loaders:['style-loader','css-loader'] }, ] } } 复制代码

解释配置

test:/\.css$/:当我们遇到以.css结尾的文件,我们就走下面的loader。

css-loader将css的文件集合在一起,然后由style-loader将css代码转换成字符串插入到我们的输出文件main.js里面。

scss

css已经不能满足我们了,我们要用功能更强大的scss!

安装

yarn add sass-loader style-loader css-loader node-sass

配置


module.exports={ module: { rules: [ { test:/\.scss$/, loaders:['style-loader','css-loader','sass-loader'] }, ] } } 复制代码

解释配置

sass-loader先将scss代码编译成css代码,css-loader将css的文件集合在一起,然后由style-loader将css代码转换成字符串插入到我们的输出文件main.js里面。

image

安装

yarn add file-loader url-loader

配置


module.exports={ module: { rules: [ { test:/\.(jpg|png|jpeg)$/, use: { loader: "url-loader", options: { outputPath:'images', name:'[name].[ext]', limit: 2048 } } ] } } 复制代码

解释配置

我们遇到jpg png jpeg结尾的,我们就走下面的配置!

如果我的图片大小是大于limit: 2048 2kb的,我就在dist目录下创建一个images的文件夹,文件夹里面放我用file-loader打包的图片,名字是'[name].[ext]',[name]就是我配置的入口文件名,.[ext]我们的图片后缀。

如果我的图片大小是小于2kb的,我就用url-loader,url-loader会将图片转换成base64,插入在main.js里面。

小图片的base64转换是没有意义的,因为小图片被base64转换了之后,大小反而变得更大了点。

plugin

plugin是用来拓展webpack的功能的!

html-webpack-plugin

安装

yarn add html-webpack-plugin

配置


const HtmlPlugin = require('html-webpack-plugin') plugins:[ new HtmlPlugin({ template: "index.html" }) ] 复制代码

解释配置

template: "index.html"用我们当前目录下的index.html文件作为模版

在打包之后生成的dist文件夹里面生产一个同样模版的index.html文件。

clean-webpack-plugin

安装

yarn add clean-webpack-plugin

配置


const {CleanWebpackPlugin} = require('clean-webpack-plugin') plugins:[ new CleanWebpackPlugin({}) ] 复制代码

解释配置

每次打包之前先删除dist文件夹!

watch

在开发的时候,每次修改代码都要自己去重新打包,预览。

这样真的好麻烦!😓

我们期望,如果可以我一修改代码,代码就自己自动重新构建,然后页面马上也跟着刷新了!

提升工作效率,优化开发体验

安装

配置


"script":{ "start":"webpack --watch" } 复制代码

解释配置

执行npm start,运行index.html,我们的文件就处于监听中!

当我们修改了代码之后,在浏览器,重新手动刷新一下页面,我们就可以看到最新的修改了!

devServer

每次修改了代码之后,还要手动重新刷新一下浏览器,太麻烦了!

而且,文件系统不能发ajax请求!这是一个让人头大的问题!

安装

yarn add webpack-dev-server

配置


devServer: { contentBase:'./dist', hot:true, hotOnly:true, open:true, historyApiFallback: true, overlay:true, progress: true } 复制代码

"script":{ "start":"webpack-dev-server" } 复制代码

解释配置

webpack-dev-server只能在开发环境下面用!

devServer里面的配置也可以换一种方式写,写到script上面"start":"webpack-dev-server --hot --open"

当然,devServer里面很多配置都是默认自带的:

  1. contentBase:'./dist':在dist目录下,开启服务器
  2. hot:true开启热更新模式!当你修改了代码,你再也不用手动刷新页面了,浏览器会自动帮忙刷新!
  3. hotOnly:true:即使HMR不生效,浏览器也不自动刷新
  4. historyApiFallback: true:如果我们的页面发生404了,就会去index.html页面,而不是直接抛一个错误页面
  5. open:true:当我们打包完成,自动打开浏览器,自动加载我们的index.html页面
  6. overlay:true:如果代码发生了错误,直接把错误情况显示在浏览器的页面上!
  7. progress: true:显示你打包的进程

注意! 如果css代码已经从main.js里面分离出来成为一个css文件了,那么css代码的热加载是不起作用的!

HMR

虽然我们有了一个特别好的 webpack-dev-server --hot

但是hot功能,每次自动刷新浏览器的时候,都是加载全部的资源!就是相当于重新刷新了一次页面!

但是,我们希望,假如:如果我们只修改了css文件,那么就重新加载css文件好了!

只替换我们更新的模块!

Hot Module Replacement = HMR

安装

配置


const webpack = require('webpack') plugins: [ new webpack.HotModuleReplacementPlugin() ] 复制代码

在我们的index.js入口文件里面再塞一个:


if (module.hot) { module.hot.accept(); } 复制代码

解释配置

  1. HotModuleReplacementPlugin可以实现热模块的更新,当我们更新了代码的时候,浏览器network加载我们生成的hot.update的js和json文件。而不是之前的所有资源都再重新加载一次!
  2. 我们必须在某个文件接受module.hot.accept(),如果没有文件接受,就不会生成热模块替换的文件。
  3. 为什么我们的css不需要写module.hot.accept(),是因为css-loader已经为我们完成了这一项操作
  4. 我们可以在module.hot监听是哪个文件发生了修改,再做自己想做的操作:

if (module.hot) { console.log('修改了...') module.hot.accept('@components/child', () => { ReactDom.render( <App/>,document.getElementById('root')) }); } 复制代码
比如我监听到了`'@components/child'`这个文件发生了修改,那么我就重新render一下页面!

jsx

安装

yarn add babel-loader

配置


{ test:/\.(js|jsx)$/, exclude:/node_modules/, loader: 'babel-loader' } 复制代码

解释配置

排除node_modules文件夹下的,以jsjsx结尾的文件,我们要用babel-loader将es6的代码转换成es5的!

tsx

安装

yarn add awesome-typescript-loader

配置


{ test:/\.(ts)x$/, exclude:/node_modules/, loader: "awesome-typescript-loader" } 复制代码

解释配置

排除node_modules文件夹下的,以tstsx结尾的文件,我们要用awesome-typescript-loader将ts代码转换成可以编译的js代码

react

安装

yarn add react react-dom @babel/preset-env @babel/preset-react

配置

创建.babelrc文件


{ "presets": ["@babel/preset-env", "@babel/preset-react"] } 复制代码

解释配置

配置好了就可以用react了….

resolve为代码的引入带来的方便

安装

配置


resolve: { extensions: ['.js','.jsx','.tsx'], mainFiles: ['index'], alias: { '@components':path.resolve(__dirname, 'src/components'), '@pages': path.resolve(__dirname, 'src/pages'), '@assets': path.resolve(__dirname, 'src/assets') } } 复制代码

解释配置

  1. extensions: ['.js','.jsx','.tsx']: 以js,jsx,tsx文件结尾的,我们在import的时候,可以不用写后缀!
  2. mainFiles: ['index']:如果这个文件叫做index,那么我们可以不用写文件名,直接import上一级的文件夹名就可以了
  3. alias:我们做import引入的时候,如果我们改变了文件的路径,那么引入的路径也要改,路径改很麻烦,所以我们使用alias。如果引入路径为src/components,我们可以直接用@components代替!

动态chunk

安装

配置


output: { path: path.resolve(__dirname, 'dist'), filename: "[name].js", chunkFilename: "[name].chunk.js" } 复制代码

解释配置

chunkFilename: "[name].chunk.js" 当你遇到动态引入的模块的时候,这个chunkFilename就会起作用!

如何动态引入?

两种动态引入的方式,一种自己写的,一种react自带的。

  1. 自己写的

const getAsyncComponent =(load)=>{ return class AsyncComponent extends React.Component{ componentDidMount() { load().then(({default: Component})=>{ this.setState({ Component }) }) } render() { const {Component} = this.state || {} return Component ? <Component {...this.props}/> : <div>loading...</div> } } } const asyncUser = getAsyncComponent(()=>import(/* webpackChunkName:'page-user'*/'@pages/user')) 复制代码
  1. react自带的 Suspense lazy

lazy(()=>import(/* webpackChunkName:'page-user'*/'@pages/user')) 复制代码
  1. 全部代码

import React, { Suspense, Component, lazy } from 'react' import ReactDom from 'react-dom' import './index.scss' import { Route, BrowserRouter, Switch } from 'react-router-dom' import Home from '@pages/home'; // import {User} from "@pages/user"; // import {About} from "@pages/about"; // 如果不注销这个同步的import,那么chunk就不能动态生成... // const asyncUserComponent = ()=>import(/* webpackChunkName: 'page-user' */'@pages/user').then(({default: component})=> component()) const getAsyncComponent =(load)=>{ return class AsyncComponent extends React.Component{ componentDidMount() { load().then(({default: Component})=>{ this.setState({ Component }) }) } render() { const {Component} = this.state || {} return Component ? <Component {...this.props}/> : <div>loading...</div> } } } const asyncUser = getAsyncComponent(()=>import(/* webpackChunkName:'page-user'*/'@pages/user')) const asyncAbout = getAsyncComponent(()=>import(/* webpackChunkName:'page-about'*/'@pages/about')) class App extends React.Component{ render(){ return ( <Suspense fallback={<div>loading...</div>}> <BrowserRouter> <Switch> <Route exact path='/' component={Home}/> <Route path='/user' component={lazy(()=>import(/* webpackChunkName:'page-user'*/'@pages/user'))}/> <Route path='/about' component={asyncAbout}/> </Switch> </BrowserRouter> </Suspense> ) } } ReactDom.render(<App/>,document.getElementById('root')) 复制代码
  1. 解释

()=>import(/* webpackChunkName:'page-user'*/'@pages/user') 复制代码
`webpackChunkName`就是chunk的名称,最后这个chunk会生成一个文件,文件名叫做`page-user.chunk.js`

静态chunk

静态chunk就是传统import方式引入的chunk

比如:import React from 'react'

安装

配置


optimization: { usedExports: true, splitChunks: { chunks: "all", cacheGroups: { vendors:{ test:/node_modules/, priority:-10, }, ui:{ test:/src\/components/, minSize:0, reuseExistingChunk: true, priority:-20 } } } } 复制代码

解释配置

如果这个import的模块是属于node_modules目录下的,就塞到vendors模块下,打包出来的文件名就叫做:vendors~main.chunk.js

如果这个import的模块是属于src/components目录下的,就塞到ui模块下,打包出来的文件名就叫做:ui~main.chunk.js

压缩js

这个功能一般是用到生产模式下的

安装

yarn add terser-webpack-plugin

配置


const TerserJSPlugin = require("terser-webpack-plugin"); optimization:{ minimizer: [ new TerserJSPlugin({}) ] }, 复制代码

解释配置

压缩js代码

css分离文件

安装

yarn add mini-css-extract-plugin

配置


const MiniCssExtractPlugin = require("mini-css-extract-plugin"); module: { rules: [ { test:/\.scss$/, loaders:[MiniCssExtractPlugin.loader,'css-loader','sass-loader'] }, { test:/\.css$/, loaders:[MiniCssExtractPlugin.loader,'css-loader'] }, ] } plugins: [ new MiniCssExtractPlugin({ filename: "[name].css", chunkFilename: "[id].css" }) ], 复制代码

解释配置

把css代码从main.js文件里面剥离出一个单独的css文件。

压缩css

一般用在生产模式下。

安装

yarn add optimize-css-assets-webpack-plugin

配置


optimization:{ minimizer: [ new OptimizeCSSAssetsPlugin({}) ] } 复制代码

解释配置

css代码被压缩了

DllPlugin

我们只希望第三方的模块在第一次打包的时候分析,以后都不分析了。

加快打包的速度!

安装

yarm add add-asset-html-webpack-plugin

配置

  1. 创建webpack.dll.js

const {DllPlugin} = require('webpack') const path = require('path') module.exports={ mode:'production', entry:{ react:['react','react-dom'], time:['timeago.js'] }, output:{ filename: "[name].dll.js", path: path.resolve(__dirname, 'dll'), library: '[name]' }, plugins:[ new DllPlugin({ name:'[name]', path: path.resolve(__dirname, './dll/[name].manifest.json') }) ] } 复制代码
  1. "dll": "webpack --config webpack.dll.js"

  2. 配置webpack.config.js


const fs = require('fs') const {DllReferencePlugin} = require('webpack') const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin') const dllPlugins = ()=>{ const plugins = [] const files = fs.readdirSync(path.resolve(__dirname, './dll')) files.forEach(file => { if (/.*\.dll.js/.test(file)){ plugins.push(new AddAssetHtmlPlugin({ filepath: path.resolve(__dirname, './dll',file) })) } if (/.*\.manifest.json/.test(file)){ plugins.push(new DllReferencePlugin({ manifest:path.resolve(__dirname, './dll', file) })) } }) return plugins; } plugins: [ ...dllPlugins() ] 复制代码

解释配置

1.注意

先运行yarn run dll,这样先解析webpack.dll.js,生成了dll文件夹以及关于dll的文件。

再执行yarn start,这样在运行webpack.config.js的时候,执行到fs.readdirSync(path.resolve(__dirname, './dll'))这一步才不会因为找不到文件夹文件而出错!

2.DllReferencePlugin :

意思是当我们打包的时候,我们发现第三方的模块,我们之前会从node_modules里面一遍一遍的找

现在我们会先从dll/vendors.manifest.json里面找映射关系

如果第三方模块在映射关系里,我们知道,这个第三方模块,就在vendors.dll.js里面,

那么就会从全局变量里面拿, 因为第三方模块第一次打包的时候,就生成里全局变量了

就不用再在node_modules里面一点一点分析,一点一点找了,加快了打包的速度

3.AddAssetHtmlPlugin:

最后把我么打包生成的*.dll.js文件,作为静态文件插入到我们的index.html里面

分离环境

开发环境跟到生产环境是不一样的,有些东西开发环境能用,生产环境不见得能用,比如devServer但是有些代码又是公用的,比如css-loader

开发环境跟到生产环境注重的东西也不一样。开发环境更注重写代码的效率,方便。生产环境更注重包的大小,轻便。

所以,要针对不同的环境做不同的配置!

安装

yarn add webpack-merge

配置

比如生产环境:(开发环境类似啦)


const merge = require('webpack-merge') const baseConfig = require('./webpack.base') const prodConfig = {...} module.exports=merge(baseConfig, prodConfig) 复制代码

解释配置

在不同的环境,根据不同的侧重,做不同的事!

最后

继续学习

深夜被女友手把手入门webpack的感觉

作者:西门吹喵
链接:https://juejin.im/post/5e69e1f3f265da57663fe9f2

看完两件小事

如果你觉得这篇文章对你挺有启发,我想请你帮我两个小忙:

  1. 关注我们的 GitHub 博客,让我们成为长期关系
  2. 把这篇文章分享给你的朋友 / 交流群,让更多的人看到,一起进步,一起成长!
  3. 关注公众号 「画漫画的程序员」,公众号后台回复「资源」 免费领取我精心整理的前端进阶资源教程

JS中文网是中国领先的新一代开发者社区和专业的技术媒体,一个帮助开发者成长的社区,目前已经覆盖和服务了超过 300 万开发者,你每天都可以在这里找到技术世界的头条内容。欢迎热爱技术的你一起加入交流与学习,JS中文网的使命是帮助开发者用代码改变世界

本文著作权归作者所有,如若转载,请注明出处

转载请注明:文章转载自「 Js中文网 · 前端进阶资源教程 」https://www.javascriptc.com

标题:深夜被女友手把手入门webpack的感觉

链接:https://www.javascriptc.com/3947.html

« React Hook 实战指南!(4)
用20个小示例带你重学 ES10 新特性»
Flutter 中文教程资源

相关推荐

QR code