如何用webpack搭建vue項目?本文案例詳解


 

前言:都2020年了,感覺是時候該學一波webpack了,趁着最近有時間,就學了一下,等把官網上的webpack結構和模塊大概看了一遍之后,就感覺可以開始搭個項目實戰一下了,從0開始,一步步記錄下來

使用版本: webpack4.x

1.包含插件和loader

* html:    
  html-webpack-plugin clean-webpack-plugin

* css:    
  style-loader css-loader sass-loader node-sass postcss-loader autoprefixer

* js:      
  babel-loader @babel/preset-env @babel/core @babel/polyfill core-js@2 @babel/plugin-transform-runtime
  @babel/runtime  @babel/runtime-corejs2

* vue:     
  vue-loader vue-template-compiler vue-style-loader vue vue-router axios vuex

* webpack: 

  file-loader url-loader webpack-dev-server webpack-merge copy-webpack-plugin happypack HardSourceWebpackPlugin 
  webpack-bundle-analyzer optimize-css-assets-webpack-plugin  portfinder  FriendlyErrorsPlugin
另外要注意 :光理論是不夠的。在此贈送2020最新企業級 Vue3.0/Js/ES6/TS/React/node等實戰視頻教程,想學的可進裙 519293536 免費獲取,小白勿進哦

2.webpack功能

-- 生成hmtl模板
-- 刪除上一次的dist文件
-- 自動添加瀏覽器前綴
-- 使用sass預編譯器
-- 轉換ES6,7,8,9語法為ES5
-- 大於10k文件打包到dist,小於10k轉換為base64
-- 兼容vue-- 拷貝靜態文件
-- 熱更新
-- 區分當前環境
-- 多線程打包
-- 緩存未改變模塊
-- g-zip壓縮
-- 獲取本機ip
-- 打包大小分析
-- 壓縮css
-- 檢查端口是否存在復制代碼

 

 

3.實現步驟

   1. 初體驗

 

 

 

1. 新建一個文件 取名為webpack-vue

2. cd webpack-vue npm init -y npm i -D webpack webpack-cli 3. 新建 src/main.js ,里面隨便寫點 console.log('hello,webpack') 4. 修改 package.json - >scripts ,添加 "build":"webpack src/main.js" 5. 然后 npm run build 如果多了一個dist文件,那么初次打包就成功了復制代碼

 

   2. 配置

  1.  新建一個 build 文件夾,新建一個 webpack.config.js
  2.  寫入以下內容  

 

 

 

const path=require('path') module.exports = { mode:'development', entry:path.resolve(__dirname,'../src/main.js'), //入口文件 output:{ filename:'[name].[hash:8].js', //打包后的名字 生成8位數的hash path.resolve(__dirname,'../dist') //打包的路徑 } } 然后修改 package.json ->scripts,修改為: "build":"webpack --config build/webpack.config.js"   然后npm run build復制代碼

 3. 我們生成了main.js之后,不可能每次都手動在index.html里面引入,所以我們需要這個插件來幫我們自動引入

先安裝插件: 

 

 

 

npm i -D html-webpack-plugin復制代碼

 

根目錄新建一個 public/index.html

修改webpack.config.js:

 

 

 

const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin') //這里引入插件 module.exports = { mode:'development', // 開發模式 entry: path.resolve(__dirname,'../src/main.js'), // 入口文件 output: { filename: '[name].[hash].js', // 打包后的文件名稱 path: path.resolve(__dirname,'../dist') // 打包后的目錄 }, //插件注入 plugins:[ new HtmlWebpackPlugin({ template:path.resolve(__dirname,'../public/index.html') }) ] }復制代碼

 

然后npm run build 就會發現dist里面多了index.html,並且已經自動幫我們引入了main.js

4. 由於hash每次生成的不同,導致每次打包都會將新的main.js打包到dist文件夾,所以我們需要一個插件來打包前刪除dist文件

先安裝插件: 

 

 

 

 

npm i -D clean-webpack-plugin復制代碼

 

 

 

webpack.config.js

const {CleanWebpackPlugin} = require('clean-webpack-plugin') //引入 plugins:[ new HtmlWebpackPlugin({ template:path.resolve(__dirname,'../public/index.html') }), new CleanWebpackPlugin() ] 復制代碼

 

5.我們一般把不需要打包的靜態文件放在public里面,這個地方不會被打包到dist,所以我們需要使用插件來把這些文件復制過去

先安裝插件:

npm i -D  copy-webpack-plugin復制代碼

 

 

 

webpack.config.js

const CopyWebpackPlugin = require('copy-webpack-plugin') // 復制文件 plugins: [ new CopyWebpackPlugin({ patterns: [ { from: path.resolve(__dirname, '../public'), to: path.resolve(__dirname, '../dist') } ] }) ]復制代碼

 

6. 為了讓webpack識別css,我們需要安裝loader,並將解析后的css插入到index.html里面的style

先安裝:

 

 

 

npm i -D style-loader css-loader復制代碼

 

 

 

 

 webpack.config.js

module.exports = {
    module:{
        rules:[{
          test:/\.css$/, use:['style-loader','css-loader'] // 從右向左解析原則 }] } }復制代碼

 

7.我們這里可以使用預編譯器更好的處理css,我這里使用的是sass

先安裝:

npm install -D sass-loader node-sass

 

 

 

 webpack.config.js

module:{
    rules: [{
        test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader'] // 從右向左解析原則 }] }復制代碼

 

8. 自動添加瀏覽器前綴

先安裝:

npm i -D postcss-loader autoprefixer

 

 

 

webpakc.config.js

  module: {
      rules: [
        {
          test: /\.scss$/, use: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader'] // 從右向左解析原則 sass-loader必須寫后面 }] } 在根目錄新建 .browserslistrc 這個里面配置兼容的瀏覽器 這里貼下我的默認配置,可以根據實際情況自己去配置 > 1% last 2 versions not ie <= 8 再新建一個postcss.config.js module.exports = { plugins: [require('autoprefixer')] // 引用該插件即可了 } 這樣打包后就自動幫你添加瀏覽器前綴了復制代碼

 

9.之前的style-loader只是把css打包到index.html里面的style-loader里面,如果css特別多這種辦法肯定不行,所以我們需要單獨抽離提取css

先安裝:

npm i -D mini-css-extract-plugin

 

 

 

 webpack.config.js

const MiniCssExtractPlugin = require("mini-css-extract-plugin") //提取css module: { rules: [{ test: /\.scss$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'sass-loader'] // 從右向左解析原則 }] } plugins: [ new MiniCssExtractPlugin({ filename: "css/[name].[hash:8].css", chunkFilename: "[id].css", }) ] 這樣打包后就將css打包到css文件下里面了復制代碼

 

10. 我們為了減少圖片字體等打包的大小,我們可以使用url-loader將少於指定大小的文件轉換為base64,使用file-loader將大於指定大小的文件 移動到指定的位置

先安裝:

npm i -D file-loader url-loader

 

 

 

 webpack.config.js

 module:{
    rules:[
      {
        test:/\.(jpe?g|png|gif|svg)(\?.*)?$/ //圖片 use:[{ loader:'url-loader', options:{ limit:10240, fallback:{ loader:'file-loader', options:{ name:'img/[name].[hash:8].[ext]', publicPath: '../' //為了防止圖片路徑錯誤 } } } }] },{ test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, use: [{ loader: 'url-loader', options: { limit: 10240, fallback: { loader: 'file-loader', options: { name: 'media/[name].[hash:8].[ext]', publicPath: '../' } } } }] },{ test:/\.(woff2?|eot|ttf|otf)(\?.*)$/, loader:'url-loader', options:{ limit:10240, fallback:{ loader:'file-loader, options:{ name:'fonts/[name].[hash:8].[ext]', publicPath: '../' } } } } ] } webpack只會轉換和移動項目中使用了的圖片 打包后發現圖片字體等也打包進去了復制代碼

 

11.為了兼容瀏覽器,我們需要將ES6,7,8,9轉換為Es5

先安裝:

 

 

 

 

npm install -D babel-loader @babel/preset-env @babel/core復制代碼
 webpack.config.js

module: {
    rules: [{
        test: /\.js$/, use: ['babel-loader'] }] } 根目錄新建 .babelrc { "presets": [ "@babel/preset-env" ] } 一些新的api如Promise,set,Maps等還不支持轉換,所以我們需要另一個插件babel/polyfill,但是這個插件會將所有的poly都打包到 mian.js里面,所以我們需要另一個插件 core-js@2 來按需加載 `npm i -s @babel/polyfill core-js@2` 修改.babelrc { "presets": [ [ "@babel/preset-env", { "modules": false, "useBuiltIns": "usage", "corejs": 2, "targets": { "browsers": [ "last 2 versions", "ie >= 10" ] } } ] ] } 還有一個問題,會影響全局依賴,所以我們需要另一個插件來解決這個問題 npm i @babel/plugin-transform-runtime -D npm i --save @babel/runtime npm i --save @babel/runtime-corejs2 修改.babelrc { "presets": [ "@babel/preset-env" ], "plugins": [ [ "@babel/plugin-transform-runtime", { "helpers": true, "regenerator": true, "useESModules": false, "corejs": 2 } ] ] } 這樣就完美解決ES6新api的兼容問題了復制代碼

12.兼容vue

先安裝:

 

 

 

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

 npm i -S vue 復制代碼

 

 

 

 

webpack.config.js

const vueLoaderPlugin=require('vue-loader/lib/plugin') function resolve(dir) { return path.join(__dirname, '..', dir) } module:{ rules:[{ test:/\.vue$/, use:[vue-loader] }] }, resolve:{ alias:{ 'vue$':'vue/dist/vue.runtime.esm.js', '@':path.resolve(__dirname,'../src') }, extensions: ['.js', '.vue', '.json'], }, plugins:[ new vueLoaderPlugin() ] 這樣配置完成之后就可以webpack就識別了vue文件復制代碼

 

13.熱更新

  先安裝:

 

 

 

 npm i -D webpack-dev-server復制代碼

 

 

 

 

 wepack.config.js

const webpack = require('webpack') devServer: { compress: true, port: 8989, hot: true, inline: true, hotOnly: true, //當編譯失敗時,不刷新頁面 overlay: true, //用來在編譯出錯的時候,在瀏覽器頁面上顯示錯誤 publicPath: '/', //一定要加 open: true, watchOptions: { // 不監聽的文件或文件夾,支持正則匹配 ignored: /node_modules/, // 監聽到變化后等1s再去執行動作 aggregateTimeout: 1000, // 默認每秒詢問1000次 poll: 1000 } }, plugins: [ new webpack.HotModuleReplacementPlugin(), new webpack.NamedModulesPlugin(), new webpack.NoEmitOnErrorsPlugin() ] 在package.json里面配置: "dev":"webpack-dev-server --config build/webpack.config.js" 在main.js里面 import Vue from "vue" import App from "./App" new Vue({ render:h=>h(App) }).$mount('#app') src文件夾內新建一個APP.vue,內容自定義 然后npm run dev 頁面就運行起來了復制代碼

 

14.區分開發環境和生產環境

build文件夾內新建 webpack.dev.js  webpack.prod.js復制代碼
開發環境:

1.不需要壓縮代碼
2.熱更新
3.完整的soureMap
...

生產環境:

1.壓縮代碼
2.提取css文件
3.去除soureMap (根據個人需要)
...復制代碼

我們這里需要安裝 webpack-merge 來合並配置項

先安裝:

 

 

 

npm i -D webpack-merge   復制代碼

 

 

 

 

webpack.dev.js

const webpackConfig = require('./webpack.config') const merge = require('webpack-merge') const webpack = require('webpack') module.exports = merge(webpackConfig, { mode: 'development', devtool: 'cheap-module-eval-source-map', devServer: { compress: true, port: 8989, hot: true, inline: true, hotOnly: true, //當編譯失敗時,不刷新頁面 overlay: true, //用來在編譯出錯的時候,在瀏覽器頁面上顯示錯誤 publicPath: '/', //一定要加 open: true, watchOptions: { // 不監聽的文件或文件夾,支持正則匹配 ignored: /node_modules/, // 監聽到變化后等1s再去執行動作 aggregateTimeout: 1000, // 默認每秒詢問1000次 poll: 1000 } }, module: { rules: [ { test: /\.js$/, use: ['babel-loader'] }, { test: /\.css$/, use: ['vue-style-loader', 'css-loader', 'postcss-loader'], }, { test: /\.scss$/, use: ['vue-style-loader', 'css-loader', 'postcss-loader', 'sass-loader'], exclude: /node_modules/ } ] }, plugins: [ new webpack.HotModuleReplacementPlugin(), new webpack.NamedModulesPlugin(), new webpack.NoEmitOnErrorsPlugin() ] })復制代碼

 

 

 

 

 webpack.prod.js

const webpackConfig = require('./webpack.config') const merge = require('webpack-merge') const path = require('path') const { CleanWebpackPlugin } = require('clean-webpack-plugin') //清除dist const MiniCssExtractPlugin = require("mini-css-extract-plugin") //提取css function resolve(dir) { return path.join(__dirname, '..', dir) } module.exports = merge(webpackConfig, { mode: "production", devtool: 'none', optimization: { splitChunks: { chunks: 'all', cacheGroups: { vendors: { name: 'vendors', test: /[\\\/]node_modules[\\\/]/, priority: -10, chunks: 'initial' }, common: { name: 'chunk-common', minChunks: 2, priority: -20, chunks: 'initial', reuseExistingChunk: true } } } }, module: { rules: [ { test: /\.js$/, use: ['babel-loader'] exclude: /node_modules/, include: [resolve('src'), resolve('node_modules/webpack-dev-server/client')] }, { test: /\.css$/, use: [ { loader: MiniCssExtractPlugin.loader, options: { publicPath: '../', } }, 'css-loader', 'postcss-loader'], }, { test: /\.scss$/, use: [ { loader: MiniCssExtractPlugin.loader, options: { publicPath: '../', } }, 'css-loader', 'postcss-loader', 'sass-loader'], exclude: /node_modules/ } ] }, plugins: [ new CleanWebpackPlugin(), new MiniCssExtractPlugin({ filename: 'css/[name].[hash].css', chunkFilename: 'css/[name].[hash].css', }) ] }) 復制代碼

 

  webpack.config.js

const path = require('path') const HtmlWebpackPlugin = require('html-webpack-plugin') //這里引入插件 const vueLoaderPlugin = require('vue-loader/lib/plugin') const CopyWebpackPlugin = require('copy-webpack-plugin') // 復制文件 function resolve(dir) { return path.join(__dirname, '..', dir) } module.exports = { mode: 'development', entry: path.resolve(__dirname, '../src/main.js'), output: { filename: 'js/[name].[hash:8].js', path: path.resolve(__dirname, '../dist'), chunkFilename: 'js/[name].[hash:8].js', //異步加載模塊 publicPath: './' }, externals: {}, module: { noParse: /jquery/, rules: [ { test: /\.vue$/, use: [{ loader: 'vue-loader', options: { compilerOptions: { preserveWhitespace: false } } }] }, { test: /\.(jpe?g|png|gif)$/i, //圖片文件 use: [ { loader: 'url-loader', options: { limit: 10240, fallback: { loader: 'file-loader', options: { name: 'img/[name].[hash:8].[ext]', publicPath: '../' } } } } ] }, { test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, //媒體文件 use: [ { loader: 'url-loader', options: { limit: 10240, fallback: { loader: 'file-loader', options: { name: 'media/[name].[hash:8].[ext]', publicPath: '../' } } } } ] }, { test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i, // 字體 use: [ { loader: 'url-loader', options: { limit: 10240, fallback: { loader: 'file-loader', options: { name: 'font/[name].[hash:8].[ext]', publicPath: '../' } } } } ] } ] }, resolve: { alias: { 'vue$': 'vue/dist/vue.esm.js', '@': resolve('src'), }, extensions: ['.js', '.vue', '.json'], }, //插件注入 plugins: [ new HtmlWebpackPlugin({ template: path.resolve(__dirname, '../public/index.html') }), new vueLoaderPlugin(), new CopyWebpackPlugin({ patterns: [ { from: path.resolve(__dirname, '../public'), to: path.resolve(__dirname, '../dist') } ] }) ] }復制代碼

3.webpack配置優化

1.設置mode

默認為production,webpack4.x默認會壓縮代碼和去除無用的代碼

可選參數:production, development

ps:之前我認為只需要設置mode為production,就不用使用壓縮css和js的插件,但結果發現我錯了,仔細比較了下,還是要安裝的

先安裝打包css的:

 

 

 

 

npm i -D optimize-css-assets-webpack-plugin復制代碼
webpack.prod.js

const optimizeCss = require('optimize-css-assets-webpack-plugin'); plugins:[ new optimizeCss({ cssProcessor: require('cssnano'), //引入cssnano配置壓縮選項 cssProcessorOptions: { discardComments: { removeAll: true } }, canPrint: true //是否將插件信息打印到控制台 }) ] 復制代碼

壓縮js和js打包多線程的暫時沒有添加,在網上搜有的說不用添加,有的說還是要安裝插件,等實際項目中我用完之后再來添加

2.縮小搜索范圍

 alias 可以告訴webpack去指定文件夾去尋找,盡量多使用 include exclude 包括和過濾 noParse 當我們代碼中使用到import jq from 'jquery'時,webpack會去解析jq這個庫是否有依賴其他的包。這個可以告訴webpack不必解析 extensions 使用頻率高的寫在前面 復制代碼

3.單線程轉多線程

webpack處理文本,圖片,css的時候,由於js單線程的特性,只能一個一個文件的處理,HappyPack可以將這個任務 分解到多個子線程里面,子線程完畢后會把結果發送到主線程,從而加快打包速度

先安裝:

npm i -D happypack

webpack.prod.js

const HappyPack = require('happypack') //單進程轉多進程 const os = require('os') const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length }) module: { rules: [{ test: /\.js$/, use: ['happypack/loader?id=happyBabel'], exclude: /node_modules/, include: [resolve('src'), resolve('node_modules/webpack-dev-server/client')] }] } plugins:[ new HappyPack({ id: 'happyBabel', loaders: ['babel-loader?cacheDirectory'], threadPool: happyThreadPool }) ] 復制代碼

4.第三方模塊優化

將不怎么改變的第三方依賴,我們可以用DllPlugin DllReferencePlugin將它從依賴中抽離出來,這樣每一次打包就不用打包這些文件,加快了打包的速度;

但是webpack4.x的性能已經很好了,參考vue-cli也沒有使用dll抽離,所以我們這里也不使用了,這里我們使用 另一個插件:hard-source-webpack-plugin ,這個插件會去對比修改了哪些配置,只去打包修改過了的配置 第一次打包速度正常,第二次打包速度能提升 50%+

npm i -D hard-source-webpack-plugin

webpack.prod.js

const HardSourceWebpackPlugin = require('hard-source-webpack-plugin') //緩存第三方模塊 plugins: [ new HardSourceWebpackPlugin() ] 復制代碼

5.externals

通過cdn加載的依賴,可以在這里設置,就不會通過webpack編譯

6.g-zip壓縮

g-zip壓縮可以將已經壓縮過的js,css再次壓縮一遍,減少了打包大小,需要nginx配置

npm i -D compression-webpack-plugin

webpack.prod.js

const CompressionWebpackPlugin = require('compression-webpack-plugin') const productionGzipExtensions = ["js", "css"]; plugins:[ new CompressionWebpackPlugin({ filename: '[path].gz[query]', algorithm: 'gzip', test: new RegExp("\\.(" + productionGzipExtensions.join("|") + ")$"), threshold: 10240, // 只有大小大於10k的資源會被處理 minRatio: 0.6 // 壓縮比例,值為0 ~ 1 }) ] 復制代碼

7.自動獲取本地Ip,通過本地ip地址啟動項目

webpack.dev.js

const os = require('os') devServer:{ host:()=>{ var netWork = os.networkInterfaces() var ip = '' for (var dev in netWork) { netWork[dev].forEach(function (details) { if (ip === '' && details.family === 'IPv4' && !details.internal) { ip = details.address return; } }) } return ip || 'localhost' } } 復制代碼

8.抽離一些公共配置

根目錄新建vue.config.js 里面配置一些公共的配置如:

const os = require('os') module.exports = { dev: { host: getNetworkIp(), //端口號 port: 8999, autoOpen: true, //自動打開 }, build: { productionGzipExtensions: ["js", "css"], //需要開啟g-zip的文件后綴 productionGzip: false //是否開啟g-zip壓縮 } } //獲取本地ip地址 function getNetworkIp() { var netWork = os.networkInterfaces() var ip = '' for (var dev in netWork) { netWork[dev].forEach(function (details) { if (ip === '' && details.family === 'IPv4' && !details.internal) { ip = details.address return; } }) } return ip || 'localhost' } 然后webpack.dev.js webpack.prod.js引入這個文件,獲取其中的配置就好了 復制代碼

9.打包大小分析

先安裝:

npm i -D webpack-bundle-analyzer

webpack.prod.js

if (process.env.npm_config_report) { const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin prodConfig.plugins.push( new BundleAnalyzerPlugin() ) } 然后 npm run build --report就會彈出一個頁面,里面就是打包大小分析 復制代碼

10.完整的vue項目(vue-router axios vuex等)

先安裝:

npm i -S vue-router axios vuex

然后在src里面新建 -> router文件夾 ->新建index.js

index.js

import Vue from "vue" import Router from "vue-router" Vue.use(Router) export default new Router({ mode: 'hash', routes: [ { path: '/', name: 'home', component: () => import(/* webpackChunkName: "home" */ "@/views/home"), }, ] }) main.js import router from "./router" new Vue({ router, render: h => h(App) }).$mount('#app') 新建views -> Home.vue 隨便寫點東西,然后npm run dev 這樣就完成了一個路由了 復制代碼

到這里webpack搭建vue項目就搭建完成,還有沒懂的嗎?光理論是不夠的。在此贈送2020最新企業級 Vue3.0/Js/ES6/TS/React/node等實戰視頻教程,想學的可進裙 519293536 免費獲取,小白勿進哦!

本文的文字及圖片來源於網絡加上自己的想法,僅供學習、交流使用,不具有任何商業用途,版權歸原作者所有,如有問題請及時聯系我們以作處理


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM