2020了,你需要懂的webpack知识[基础篇]

随着前端工程化的越来越疯狂,不会点webpack的前端真的吃不香,睡不着。

所谓webpack就是一个现代 JavaScript 应用程序的静态模块打包器。

开胃菜

吃饭前先来一道开胃菜,看完这遍文章你会学到什么?

  • 了解什么是webpack
  • 知道怎么去打包静态资源
  • 知道几个(entrt,output,module,plugins等)核心的配置作用
  • 不用vue-cli,从0到1跑起自己的vue项目
  • 不用create-react-app,从0到1跑起自己的react项目
  • 搭建一个既能跑vue项目也能跑react项目的配置

以上这些都是你将会学到的。

温馨提示:建议跟着一起敲,这会加深你对每一个知识的理解和固定; 还有内容有点多,需要你静下心来慢慢的品尝,你会收获到你意想不到的知识。

入门webpack

1.1初始化项目

先我们先在自己舒服的地方创建一个文件夹,文件夹里面初始化项目。

    mkdir myWebpackPro
    npm init -y

然后我们安装webpack webpack-cli 到项目

npm i webpack webpack-cli -D

你也可以用cnpm 或者yarn 来安装

cnpm i webpack webpack-cli -D / yarn add webpack webpack-cli -D

接着我们在myWebpack文件夹创建一个src文件夹,在里面创建一个index.js文件(这个将来将是我们的入口文件)。

mkdir src
cd src
touch index.js

最后我们在与src同级创建一个webpack.config.js(配置webpack的文件)文件

touch webpack.config.js

最终的目录结构是这样的

image

到此为止,我们初始化项目就完成了。

1.2测试webpack能否正常打包

首先,我们在index.js里面随便console.log()一句

image

然后到package.jsonscript下配置一下build命令,先删除默认的test命令,再添加build命令:

"build":"webpack ./src/index.js"
image

最后我们再终端执行npm run build命令

npm run build

如果执行成功的话在根目录会多一个dist文件夹里面会有一个main.js

image

让我们打开main.js看看有没有把我们刚刚console.log()的那句话打包进去。

image

现在让我们真正进入webpack配置。

1.3更改webpack的入口和默认打包的文件名

首先,我们打开webpack.config.js配置一下entryoutput

image

我们在顶层引进path模块,这是node内置的一个模块,无需单独安装,是用来出来文件路径的一个模块。我们的打包入口(entry)依旧是刚刚创建的那个index.js文件,出口(output)也就是打包成功后,输出的位置(path)和文件名(filename),我们输出的位置依旧选择了dist文件下,但是输出的文件名,我们该为了index.js(之后我们会将文件名改为动态的)

配置完这些后,我们到package.json改一下打包路径,将之前的./src/index.js改为./webpack.config.js

image

我们npm run build之后依旧能打包成功。

image

输出的文件名也变成了index.js,现在你会看到dist目录下还有个main.js,是不是感到很奇怪?不用奇怪,这是我们上一次打包余留下来的,等下我们将来说如何解决上一次打包余留下来的文件。

1.4新建一个index.html模版

首先,我们在src下新建一个跑public文件夹,然后在里面新建一个index.html模版(用于我们查看打包的js文件,之后再用于我们vue或者react项目的唯一html渲染模版)

cd src
mkdir public 
touch index.html

新建完之后我们将刚才打包好的js引进去看看,浏览器有没有打印出我们console.log()。

image

但是,当我们将outputfilename改成动态之后,每次打包输出的文件名都不一样,那么我们每一次打包都要重新引用一次,这样特别麻烦。

1.5html-webpack-plugin

为了解决上面说的那个问题,我们需要安装一下html-webpack-plugin插件。

npm run html-webpack-plugin -D

安装完之后我们在webpack.config.js配置一下plugins

image

这里我们配置了html-webpack-plugin,也把输入文件名改为了动态的了([name].[hash:8].js)。

我们再npm run build,然后你会看到再dist下多一个index.html。接着打开它,发现控制台输出的跟我们console.log()的值一致就对了。(记得在打包前,把之前在index.html手机引进的script标签去掉,不如打包会在控制台会报一个找不到文件名的错)

接下来,我们来解决,打包后自动清除之前打包的代码。

1.6 clean-webpack-plugin

现在你打开dist好很多冗余的js文件,每次手动清除的话会非常烦躁。那我们就让webpack来帮我们做。

image

首先,安装clean-webpack-plugin

npm i clean-webpack-plugin -D

安装完之后,我们再配置一下plugins选项。

image

注意⚠️:CleanWebpackPlugin是具名导出。

最后,我们在npm run build一下,看看dist目录下还有没有多余的js文件。

image

soga,干净。

那么,现在问题又来,我们每次改动一点js代码都要重新手动去打包一次,这样真炒鸡麻烦啊,那有没有自动打包的方法呢?那么答案来咯,当然是有的。下面我们解决这个问题。

1.7 热更新

现在我们来解决自动打包的问题。首先,我们安装一下webpack-dec-server,配合webpack内置的HotModuleReplacementPlugin插件来完成热更新。

npm i webpack-dev-server -D

HotModuleReplacementPlugin不用手动下载,webpack内置。

安装完之后,我们先来配置一下webpack.config.js文件

image

配置完webpack.config.js,我们的package.json添加一条打包命令

image

配置完后,我们现在用npm run dev命令打包,然后我们回去更新我们index.js文件的任何东西,保存之后都会自动更新。

好了,现在基础功能,我们搭建得差不多了。接下我们配置module,继续完善最后的基础功能,比如打包js,css,image

1.8 打包js文件

打包js文件,主要是要将es6,7...的语法转义成es5的语法,让不兼容es6,7...语法的浏览器也能使用。

首先我们先安装一套包babel-loader,@babel/core,@babel/preset-env,@babel/polyfill

npm i babel-loader @babel/core @babel/preset-env @babel/polyfill -D

安装完之后,我们来配置webpack.config.jsmodule

image

这里简单介绍一下,每个loader的作用,babel-laoder主要是打包js文件,而babel-loader又依赖@babel/core,@babel/preset-env可以转义es6的map,every,fifler,some...语法,但是不转义Promise,async,await...语法,所以要用@babel/polyfill来转义。

1.9 打包css文件

首先安装几个loader,style-loader,css-loader

npm i style-laoder css-loader -D

安装完,我们接着配置module

image

这里只是简单配置css文件,可能有些项目还会用到less,scss等,这里就不过多阐述,如果你项目用到可以到官方找一下对应的loader配置一下,都是大同小异。

2.0 打包image,font,media文件

首先还是先安装几个loader,url-laoder,file-loader

npm i url-loader file-loader -D

安装完,我们继续配置module

image

介绍一下这两个loader的配置,配置里我们指定了limit,如果文件没有大于这个就用url-loader,base64打包,如果大过这个值,就会用file-loader打包,同时会按options下配置的name输出到对应的位置。

好,基础配置就说那么多了,如果你的项目还有其他的需求可以到官网撸一番。

最终配置如下:


const path = require('path')

const HtmlWebpackPlugin = require('html-webpack-plugin')

const { CleanWebpackPlugin } = require('clean-webpack-plugin')

const webpack = require('webpack')

const config = {

    entry: ['@babel/polyfill', path.resolve(__dirname, './src/index.js')],

    output: {

        filename: '[name].[hash:8].js',

        path: path.resolve(__dirname, './dist')

    },

    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_module/,
                use: [
                    {
                        loader: "babel-loader",
                        options: {
                            presets: ['@babel/preset-env']
                        }
                    }
                ]
            },
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader']
            },
            {
                test: /\.(jpg|png|gif)$/,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 1024,
                            fallback: {
                                loader: 'file-loader',
                                options: {
                                    name: 'img/[name].[hash:8].[ext]'
                                }
                            }
                        }
                    }
                ]
            },
            {
                test: /\.(mp4|webm|ogg|mp3|wav)(\?.*)?$/,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 1024,
                            fallback: {
                                loader: 'file-loader',
                                options: {
                                    name: 'media/[name].[hash:8].[ext]'
                                }
                            }
                        }
                    }
                ]
            },
            {
                test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 1024,
                            fallback: {
                                loader: 'file-loader',
                                options: {
                                    name: 'fonts/[name].[hash:8].[ext]'
                                }
                            }
                        }
                    }
                ]
            }
        ]
    },

    devServer: {
        port: '3000',
        host: '127.0.0.1',
        hot: true,
        open: true
    },

    plugins: [
        new HtmlWebpackPlugin({
            filename: "index.html",
            template: path.resolve(__dirname, './src/public/index.html')
        }),
        new CleanWebpackPlugin(),
        new webpack.HotModuleReplacementPlugin()
    ]

}

module.exports = config

从0到1跑起Vue项目

当你以上的基础部分都熟悉了,我们从0到1搭建一个vue环境是soeasy的事。明人不说暗话,其实上面的基础部分一直在为跑起vue项目做铺垫。

现在我们要跑起vue项目,在以上基础的配置上,在搞几个loader就完事了,简单得我都不好意思往下说了。

好了,现在我们先来安装几个东西,vue,vue-loader,vue-template-compiler,vuw-style-loader

npm i vue vue-loader vue-template-compiler vue-style-loader -D

安装完之后,我们来配置一下weback.config.js

这是module配置,记得在顶层导入VueLoaderPlugin

image

这是plugins配置

image

配置完webpack.config.js之后,我们来在src下创建一个vue文件夹,然后在里面创建一个app.vue文件

cd src
mkdir vue
cd vue
touch App.vue

目录

image

接着在app.vue中,学上你熟悉的vue语法。

image

写好之后,我们回到入口文件也是src下的index,js,我们来重新写一下里面的逻辑,之前只是console.log()一下,什么也没做。

最后我们的index.js文件是这样的。

image

这段代码对于用vue的同学再也熟悉不过来。

接着,我们回到src下的public下的index.html加一个根元素加上一个idroot

image

最后,我们npm run dev一下,我们的vue项目就跑起来了。

从0到1跑起React项目

我们搭建React项目的配置,还是重基础配置上面配置。跑起react项目的配置比vue还要简单。

首先,我们先安装一些东西。react,react-dom,@babel/preset-react

npm i react react-dom @babel/preset-react -D

安装完后,我们配置一下webpack.config.js

它只需要配置一下module下的js项就ok了。

image

接着,我们去写一下入口文件(index.js)的逻辑。

image

这段代码对用react的同学也是熟悉不过的了。

index.html文件也根vue写的一样。

image

最后,我们npm run dev一下,我们的react项目的跑起来了。

到这里,我们自己搭建Vue项目环境配置和React项目环境配置都说完了。

那么我们怎么可以把Vue环境跟React环境统一在放下同一个环境下,在我们学Vue的时候用Vue环境,在我们写React的时候用React环境,这是我们接下要做的。

一套配置,两套环境,既可跑Vue,也可跑React

我们回头观察一下Vue的配置跟React的配置有哪里不一致,哪里是一致的。

同:

entry,output

image

image,font,media打包

image

devServer和部分Plugin

image

不同的有:

  • js的loader配置,vue没有@babel/preset-ract
  • css的loader的配置,vue用的是vue-style-loader,react用的是style-loader
  • vue的loader,react配置没有
  • plugin,vue多了一个VueLoaderPlugin,react没有

好了,现在我们找出它们的异同之处了,那就好办了。

我们需要用一个叫webapck-merge的插件,我们安装一下。

npm i webpack-merge -D

安装完之后,我们在webapck.config.js同级目录下新建两个文件webpack.vue.js,webpack.react.js,来放它们两者不同的配置。

touch webpack.vue.js
touch webpack.react.js

接着我们来写一下它们的不同配置。

webpack.vue.js

const merge = require('webpack-merge')

const webpackConfig = require('./webpack.config')

const VueLoaderPlugin  = require('vue-loader/lib/plugin')

const config = {
    module:{
        rules:[
            {
                test:/\.vue$/,
                use:['vue-loader']
            },
            {
                test: /\.js$/,
                exclude: /node_module/,
                use: [
                    {
                        loader: "babel-loader",
                        options: {
                            presets: ['@babel/preset-env']
                        }
                    }
                ]
            },
            {
                test: /\.css$/,
                use: ['vue-style-loader', 'css-loader']
            },
        ]
    },
    plugins:[
        new VueLoaderPlugin()
    ]
}

module.exports = merge(webpackConfig,config)

webpack.react.js

const merge = require('webpack-merge')

const webpackConfig = require('./webpack.config')

const config = {
    module:{
        rules:[
            {
                test: /\.js$/,
                exclude: /node_module/,
                use: [
                    {
                        loader: "babel-loader",
                        options: {
                            presets: ['@babel/preset-react','@babel/preset-env']
                        }
                    }
                ]
            },
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader']
            },
        ]
    }
}

module.exports = merge(webpackConfig,config)

最后webpack.config.js就成这样了。


const path = require('path')

const HtmlWebpackPlugin = require('html-webpack-plugin')

const { CleanWebpackPlugin } = require('clean-webpack-plugin')

const webpack = require('webpack')

const config = {

    entry: ['@babel/polyfill', path.resolve(__dirname, './src/index.js')],

    output: {

        filename: '[name].[hash:8].js',

        path: path.resolve(__dirname, './dist')

    },

    module: {
        rules: [      
            {
                test: /\.(jpg|png|gif)$/,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 1024,
                            fallback: {
                                loader: 'file-loader',
                                options: {
                                    name: 'img/[name].[hash:8].[ext]'
                                }
                            }
                        }
                    }
                ]
            },
            {
                test: /\.(mp4|webm|ogg|mp3|wav)(\?.*)?$/,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 1024,
                            fallback: {
                                loader: 'file-loader',
                                options: {
                                    name: 'media/[name].[hash:8].[ext]'
                                }
                            }
                        }
                    }
                ]
            },
            {
                test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 1024,
                            fallback: {
                                loader: 'file-loader',
                                options: {
                                    name: 'fonts/[name].[hash:8].[ext]'
                                }
                            }
                        }
                    }
                ]
            }
        ]
    },

    devServer: {
        port: '3000',
        host: '127.0.0.1',
        hot: true,
        open: true
    },

    plugins: [
        new HtmlWebpackPlugin({
            filename: "index.html",
            template: path.resolve(__dirname, './src/public/index.html')
        }),
        new CleanWebpackPlugin(),
        new webpack.HotModuleReplacementPlugin(),
    ]

}

module.exports = config

好了,现在我们把它们的配置拆分完毕了,还剩最后一步我们就大功告成了,那就是在package.json添加两条命令。

image

在我们写vue项目的时候,我们就用npm run vue,在写react项目的时候,我们就用npm run react

结尾

好了,到现在为止,我们把从基础配置到vue到react再到合并两套环境都做完了。原创不易,希望这篇文章能帮助到你。如果有不到位,欢迎在下面留言;如果把你看得睡着过去的,欢迎来找我,我给你暖被窝,哈哈哈;如果有把你说蒙的了或者还不明白的,欢迎来找我,我把我的剩余的两个口罩送一半给你。

最后,我还想问各位大佬一下这句话什么意思,一个朋友发给我的:"油城壮志忆往昔,秋风吹叶道难行。莫问前路多迷茫,龙门跃起终识君"。我百度了也没有这首诗啊🤣。

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

推荐阅读更多精彩内容