面試官:本身搭建過vue開發環境嗎?

轉自大佬:不懂代碼的攻城師javascript

開篇

前段時間,看到羣裏一些小夥伴面試的時候被面試官問到這類題目。平時你們開發vue項目的時候,相信大部分人都是使用 vue-cli腳手架生成的項目架構,而後
npm run install 安裝依賴,npm run serve啓動項目而後就開始寫業務代碼了。css

可是對項目裏的webpack封裝和配置瞭解的不清楚,容易致使出問題不知如何解決,或者不會經過webpack去擴展新功能。html

該篇文章主要是想告訴小夥伴們,如何一步一步的經過 webpack4來搭建本身的vue開發環境vue

首先咱們要知道 vue-cli生成的項目,幫咱們配置好了哪些功能?java

  1. ES6代碼轉換成ES5代碼
  2. scss/sass/less/styluscss
  3. .vue文件轉換成js文件
  4. 使用 jpgpngfont等資源文件
  5. 自動添加css各瀏覽器產商的前綴
  6. 代碼熱更新
  7. 資源預加載
  8. 每次構建代碼清除以前生成的代碼
  9. 定義環境變量
  10. 區分開發環境打包跟生產環境打包
  11. ....

1. 搭建 webpack 基本環境

該篇文章並不會細講 webpack 是什麼東西,若是還不是很清楚的話,能夠先去看看 webpack官網node

簡單的說,webpack是一個模塊打包機,能夠分析你的項目依賴的模塊以及一些瀏覽器不能直接運行的語言jsxvue等轉換成 jscss文件等,供瀏覽器使用。webpack

1.1 初始化項目

在命令行中執行 npm init 而後一路回車就好了,主要是生成一些項目基本信息。最後會生成一個 package.json 文件css3

npm init

1.2 安裝webpack

1.3 寫點小代碼測試一下webpack是否安裝成功了

新建一個src文件夾,而後再建一個main.js文件nginx

// src/main.js console.log('hello webpack')

而後在 package.json 下面加一個腳本命令git

而後運行該命令

npm run serve

若是在 dist 目錄下生成了一個main.js文件,則表示webpack工做正常

2. 開始配置功能

  • 新建一個 build 文件夾,用來存放 webpack配置相關的文件
  • build文件夾下新建一個webpack.config.js,配置webpack的基本配置
  • 修改 webpack.config.js配置

  • 修改package.json 文件,將以前添加的 serve 修改成
"serve": "webpack ./src/main.js --config ./build/webpack.config.js"

2.1 配置 ES6/7/8 轉 ES5代碼

  • 安裝相關依賴
npm install babel-loader @babel/core @babel/preset-env
  • 修改webpack.config.js配置

  • 在項目根目錄添加一個 babel.config.js 文件

  • 而後執行 npm run serve 命令,能夠看到 ES6代碼被轉成了ES5代碼了

2.1.1 ES6/7/8 Api 轉es5

babel-loader只會將 ES6/7/8語法轉換爲ES5語法,可是對新api並不會轉換。

咱們能夠經過 babel-polyfill 對一些不支持新語法的客戶端提供新語法的實現

  • 安裝
npm install @babel/polyfill
  • 修改webpack.config.js配置

在 entry 中添加 @babel-polyfill

2.1.2 按需引入polyfill

2.1.2 和 2.1.1 只須要配置一個就行

修改時間 2019-05-0五、 來自評論區 兮漫天 的提醒

  • 安裝相關依賴
npm install core-js@2 @babel/runtime-corejs2 -S
  • 修改 babel-config.js

配置了按需引入 polyfill 後,用到es6以上的函數,babel會自動導入相關的polyfill,這樣能大大減小 打包編譯後的體積

2.2 配置 scss 轉 css

在沒配置 css 相關的 loader 時,引入scsscss相關文件打包的話,會報錯

  • 安裝相關依賴
npm install sass-loader dart-sass css-loader style-loader -D

sass-loaderdart-sass主要是將 scss/sass 語法轉爲css

css-loader主要是解析 css 文件

style-loader 主要是將 css 解析到 html頁面 的 style 上

  • 修改webpack.config.js配置

2.3 配置 postcss 實現自動添加css3前綴

  • 安裝相關依賴
npm install postcss-loader autoprefixer -D
  • 修改webpack.config.js配置

  • 在項目根目錄下新建一個 postcss.config.js

2.3 使用 html-webpack-plugin來建立html頁面

使用 html-webpack-plugin來建立html頁面,並自動引入打包生成的js文件

  • 安裝依賴
npm install html-webpack-plugin -D
  • 新建一個 public/index.html 頁面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <div id="app"></div> </body> </html>
  • 修改 webpack-config.js 配置

2.4 配置 devServer 熱更新功能

經過代碼的熱更新功能,咱們能夠實現不刷新頁面的狀況下,更新咱們的頁面

  • 安裝依賴
npm install webpack-dev-server -D
  • 修改webpack.config.js配置

經過配置 devServer 和 HotModuleReplacementPlugin 插件來實現熱更新

2.5 配置 webpack 打包 圖片、媒體、字體等文件

  • 安裝依賴
npm install file-loader url-loader -D

file-loader 解析文件url,並將文件複製到輸出的目錄中

url-loader 功能與 file-loader 相似,若是文件小於限制的大小。則會返回 base64 編碼,不然使用 file-loader 將文件複製到輸出的目錄中

  • 修改 webpack-config.js 配置
    添加 rules 配置,分別對 圖片,媒體,字體文件進行配置
// build/webpack.config.js const path = require('path') const HtmlWebpackPlugin = require('html-webpack-plugin') const webpack = require('webpack') module.exports = { // 省略其它配置 ... module: { rules: [ // ... { test: /\.(jpe?g|png|gif)$/i, use: [ { loader: 'url-loader', options: { limit: 4096, fallback: { loader: 'file-loader', options: { name: 'img/[name].[hash:8].[ext]' } } } } ] }, { test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, use: [ { loader: 'url-loader', options: { limit: 4096, fallback: { loader: 'file-loader', options: { name: 'media/[name].[hash:8].[ext]' } } } } ] }, { test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i, use: [ { loader: 'url-loader', options: { limit: 4096, fallback: { loader: 'file-loader', options: { name: 'fonts/[name].[hash:8].[ext]' } } } } ] }, ] }, plugins: [ // ... ] }

3. 讓 webpack 識別 .vue 文件

  • 安裝須要的依賴文件
npm install vue-loader vue-template-compiler cache-loader thread-loader -D
npm install vue -S

vue-loader 用於解析.vue文件

vue-template-compiler 用於編譯模板

cache-loader 用於緩存loader編譯的結果

thread-loader 使用 worker 池來運行loader,每一個 worker 都是一個 node.js 進程。

  • 修改 webpack.config.js配置
// build/webpack.config.js const path = require('path') const webpack = require('webpack') const HtmlWebpackPlugin = require('html-webpack-plugin') const VueLoaderPlugin = require('vue-loader/lib/plugin') module.exports = { // 指定打包模式 mode: 'development', entry: { // ... }, output: { // ... }, devServer: { // ... }, resolve: { alias: { vue$: 'vue/dist/vue.runtime.esm.js' }, }, module: { rules: [ { test: /\.vue$/, use: [ { loader: 'cache-loader' }, { loader: 'thread-loader' }, { loader: 'vue-loader', options: { compilerOptions: { preserveWhitespace: false }, } } ] }, { test: /\.jsx?$/, use: [ { loader: 'cache-loader' }, { loader: 'thread-loader' }, { loader: 'babel-loader' } ] }, // ... ] }, plugins: [ // ... new VueLoaderPlugin() ] }
  • 測試一下
  1. 在 src 新建一個 App.vue
// src/App.vue
<template> <div class="App"> Hello World </div> </template> <script> export default { name: 'App', data() { return {}; } }; </script> <style lang="scss" scoped> .App { color: skyblue; } </style>
  1. 修改 main.js
import Vue from 'vue' import App from './App.vue' new Vue({ render: h => h(App) }).$mount('#app')
  1. 運行一下

npm run serve

4. 定義環境變量

經過 webpack提供的DefinePlugin插件,能夠很方便的定義環境變量

plugins: [
    new webpack.DefinePlugin({ 'process.env': { VUE_APP_BASE_URL: JSON.stringify('http://localhost:3000') } }), ]

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

新建兩個文件

  • webpack.dev.js 開發環境使用
  • webpack.prod.js 生產環境使用
  • webpack.config.js 公用配置

  • 開發環境與生產環境的不一樣

    5.1 開發環境

  1. 不須要壓縮代碼
  2. 須要熱更新
  3. css不須要提取到css文件
  4. sourceMap
  5. ...

    5.2 生產環境

  6. 壓縮代碼
  7. 不須要熱更新
  8. 提取css,壓縮css文件
  9. sourceMap
  10. 構建前清除上一次構建的內容
  11. ...

  • 安裝所需依賴
npm i @intervolga/optimize-cssnano-plugin mini-css-extract-plugin clean-webpack-plugin webpack-merge copy-webpack-plugin -D
  1. @intervolga/optimize-cssnano-plugin 用於壓縮css代碼
  2. mini-css-extract-plugin 用於提取css到文件中
  3. clean-webpack-plugin 用於刪除上次構建的文件
  4. webpack-merge 合併 webpack配置
  5. copy-webpack-plugin 用戶拷貝靜態資源

5.3 開發環境配置

  • build/webpack.dev.js
// build/webpack.dev.js const merge = require('webpack-merge') const webpackConfig = require('./webpack.config') const webpack = require('webpack') module.exports = merge(webpackConfig, { mode: 'development', devtool: 'cheap-module-eval-source-map', module: { rules: [ { test: /\.(scss|sass)$/, use: [ { loader: 'style-loader' }, { loader: 'css-loader', options: { importLoaders: 2 } }, { loader: 'sass-loader', options: { implementation: require('dart-sass') } }, { loader: 'postcss-loader' } ] }, ] }, plugins: [ new webpack.DefinePlugin({ 'process.env': { NODE_ENV: JSON.stringify('development') } }), ] })
  • webpack.config.js
// build/webpack.config.js const path = require('path') const webpack = require('webpack') const HtmlWebpackPlugin = require('html-webpack-plugin') const VueLoaderPlugin = require('vue-loader/lib/plugin') module.exports = { entry: { // 配置入口文件 main: path.resolve(__dirname, '../src/main.js') }, output: { // 配置打包文件輸出的目錄 path: path.resolve(__dirname, '../dist'), // 生成的 js 文件名稱 filename: 'js/[name].[hash:8].js', // 生成的 chunk 名稱 chunkFilename: 'js/[name].[hash:8].js', // 資源引用的路徑 publicPath: '/' }, devServer: { hot: true, port: 3000, contentBase: './dist' }, resolve: { alias: { vue$: 'vue/dist/vue.runtime.esm.js' }, extensions: [ '.js', '.vue' ] }, module: { rules: [ { test: /\.vue$/, use: [ { loader: 'cache-loader' }, { loader: 'vue-loader', options: { compilerOptions: { preserveWhitespace: false }, } } ] }, { test: /\.jsx?$/, loader: 'babel-loader' }, { test: /\.(jpe?g|png|gif)$/, use: [ { loader: 'url-loader', options: { limit: 4096, fallback: { loader: 'file-loader', options: { name: 'img/[name].[hash:8].[ext]' } } } } ] }, { test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, use: [ { loader: 'url-loader', options: { limit: 4096, fallback: { loader: 'file-loader', options: { name: 'media/[name].[hash:8].[ext]' } } } } ] }, { test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i, use: [ { loader: 'url-loader', options: { limit: 4096, fallback: { loader: 'file-loader', options: { name: 'fonts/[name].[hash:8].[ext]' } } } } ] }, ] }, plugins: [ new VueLoaderPlugin(), new HtmlWebpackPlugin({ template: path.resolve(__dirname, '../public/index.html') }), new webpack.NamedModulesPlugin(), new webpack.HotModuleReplacementPlugin(), ] }

5.4 生產環境配置

const path = require('path') const merge = require('webpack-merge') const webpack = require('webpack') const webpackConfig = require('./webpack.config') const MiniCssExtractPlugin = require('mini-css-extract-plugin') const OptimizeCssnanoPlugin = require('@intervolga/optimize-cssnano-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin') const CopyWebpackPlugin = require('copy-webpack-plugin') module.exports = merge(webpackConfig, { mode: 'production', devtool: '#source-map', optimization: { splitChunks: { cacheGroups: { vendors: { name: 'chunk-vendors', test: /[\\\/]node_modules[\\\/]/, priority: -10, chunks: 'initial' }, common: { name: 'chunk-common', minChunks: 2, priority: -20, chunks: 'initial', reuseExistingChunk: true } } } }, module: { rules: [ { test: /\.(scss|sass)$/, use: [ { loader: MiniCssExtractPlugin.loader }, { loader: 'css-loader', options: { importLoaders: 2 } }, { loader: 'sass-loader', options: { implementation: require('dart-sass') } }, { loader: 'postcss-loader' } ] }, ] }, plugins: [ new webpack.DefinePlugin({ 'process.env': { NODE_ENV: 'production' } }), new MiniCssExtractPlugin({ filename: 'css/[name].[contenthash:8].css', chunkFilename: 'css/[name].[contenthash:8].css' }), new OptimizeCssnanoPlugin({ sourceMap: true, cssnanoOptions: { preset: [ 'default', { mergeLonghand: false, cssDeclarationSorter: false } ] } }), new CopyWebpackPlugin([ { from: path.resolve(__dirname, '../public'), to: path.resolve(__dirname, '../dist') } ]), new CleanWebpackPlugin() ] }) 

5.5 修改package.json

"scripts": { "serve": "webpack-dev-server --config ./build/webpack.dev.js", "build": "webpack --config ./build/webpack.prod.js" },

6 打包分析

有的時候,咱們須要看一下webpack打包完成後,到底打包了什麼東西,

這時候就須要用到這個模塊分析工具了 webpack-bundle-analyzer

  • 安裝依賴
npm install --save-dev webpack-bundle-analyzer
  • 修改webpack-prod.js配置,在 plugins屬性中新增一個插件

在開發環境中,咱們是不必進行模塊打包分析的,因此咱們將插件配置在了生產環境的配置項中

  • 運行打包命令
npm run build

執行成功後會自動打開這個頁面

7. 集成 VueRouter,Vuex

  1. 首先是安裝相關依賴
npm install vue-router vuex --save

7.1 集成 Vue-Router

  • 新增視圖組件
    在 src 目錄下新增兩個視圖組件 src/views/Home.vue 和 src/views/About.vue
// src/views/Home.vue
<template> <div class="Home"> <h2>Home</h2> </div> </template> <script> export default { name: 'Home', data() { return {}; } }; </script> <style lang="scss" scoped> </style>

About.vue 內容跟 Home.vue 差很少,將裏面的 Home 換成 About 就OK了

  • 新增路由配置文件

在 src 目錄下新增一個 router/index.js 文件

// src/router/index.js import Vue from 'vue' import VueRouter from "vue-router"; import Home from '../views/Home'; import About from '../views/About'; Vue.use(VueRouter) export default new VueRouter({ mode: 'hash', routes: [ { path: '/Home', component: Home }, { path: '/About', component: About }, { path: '*', redirect: '/Home' } ] })
  • 修改 main.js 文件
// main.js import Vue from 'vue' import App from './App.vue' import router from './router' new Vue({ router, render: h => h(App) }).$mount('#app')
  • 修改 App.vue 組件
// App.vue
// 在 template 中添加
// src/App.vue
<template> <div class="App"> Hello World </div> <div> // router-link 組件 用來導航到哪一個路由 <router-link to="/Home">go Home</router-link> <router-link to="/About">go About</router-link> </div> <div> // 用於展現匹配到的路由視圖組件 <router-view></router-view> </div> </template> <script> export default { name: 'App', data() { return {}; } }; </script> <style lang="scss" scoped> .App { color: skyblue; } </style>

運行 npm run serve 命令,如沒配置錯誤,是能夠看到點擊不一樣的路由,會切換到不一樣的路由視圖

7.2 配置路由懶加載

在沒配置路由懶加載的狀況下,咱們的路由組件在打包的時候,都會打包到同一個js文件去,當咱們的視圖組件愈來愈多的時候,就會致使這個 js 文件愈來愈大。而後就會致使請求這個文件的時間變長,最終影響用戶體驗

  1. 安裝依賴
npm install @babel/plugin-syntax-dynamic-import --save-dev
  1. 修改babel.config.js
module.exports = { presets: [ [ "@babel/preset-env", { useBuiltIns: "usage" } ] ], plugins: [ // 添加這個 '@babel/plugin-syntax-dynamic-import' ] }
  1. 修改 router/index.js 路由配置文件
import Vue from 'vue' import VueRouter from "vue-router"; Vue.use(VueRouter) export default new VueRouter({ mode: 'hash', routes: [ { path: '/Home', component: () => import(/* webpackChunkName: "Home" */ '../views/Home.vue') // component: Home }, { path: '/About', component: () => import(/* webpackChunkName: "About" */ '../views/About.vue') // component: About }, { path: '*', redirect: '/Home' } ] })
  1. 運行命令 npm run build
    查看是否生成了 Home...js 文件 和 About...js 文件

7.3 集成 Vuex

  1. 在 src 目錄下新建一個 store/index.js 文件
// store/index.js import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) const state = { counter: 0 } const actions = { add: ({commit}) => { return commit('add') } } const mutations = { add: (state) => { state.counter++ } } const getters = { getCounter (state) { return state.counter } } export default new Vuex.Store({ state, actions, mutations, getters })
  1. 修改 main.js 文件 導入 vuex
// main.js import Vue from 'vue' import App from './App.vue' import router from './router' import store from './store' // ++ new Vue({ router, store, // ++ render: h => h(App) }).$mount('#app')
  1. 修改 App.vue ,查看 vuex 配置效果
// App.vue
<template> <div class="App"> <div> <router-link to="/Home">go Home</router-link> <router-link to="/About">go About</router-link> </div> <div> <p>{{getCounter}}</p> <button @click="add">add</button> </div> <div> <router-view></router-view> </div> </div> </template> <script> import { mapActions, mapGetters } from 'vuex' export default { name: 'App', data() { return {}; }, computed: { ...mapGetters(['getCounter']) }, methods: { ...mapActions(['add']) } }; </script> <style lang="scss" scoped> .App { text-align: center; color: skyblue; font-size: 28px; } </style>
  1. 運行命令 npm run serve

當點擊按鈕的時候,能夠看到咱們的getCounter一直在增長

8 總結

到目前爲止,咱們已經成功的本身搭建了一個 vue 開發環境,不過仍是有一些功能欠缺的,有興趣的小夥伴能夠交流交流。在搭建過程當中,仍是會踩不少坑的。

若是還不熟悉 webpack 的話,建議本身搭建一次。可讓本身能深刻的理解 vue-cli 替咱們作了什麼

相關文章
相關標籤/搜索