从0搭建基于Webpack4的Vue前端脚手架

前言

今天的内容是如何从零搭建基于Webpack4的Vue前端脚手架,如有问题,欢迎来交流啊!
整个过程大致分为以下几个步骤:

  1. 搭建cvue-cli v1.0,就是一个最基本的架子,目标是可以成功本地运行和打包出文件。

2.搭建cvue-cli v2.0,把vue以及less什么的都安上,目标是运行后可以看到类似官方脚手架那样的页面。

3.搭建cvue-cli v3.0,最后在做一些优化工作,css该分离的分离,文件该压缩的压缩

注意:如需要,欢迎转载,但请注明转载文章出处 \o(^o^)o

正文

搭建cvue-cli v1.0

1.初始化项目

npm init -y

2.初始化项目的目录结构,结构如下:


初始目录结构

3.修改package.json文件

{
  "name": "cvue-cli",
  "version": "1.0.0",
  "description": "",
  "private": true,
  "scripts": {
    "dev": "webpack --config build/webpack.base.config.js --mode development",
    "build": "webpack --config build/webpack.base.config.js --mode production"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}
  1. 配置webpack.base.config.js
// 先安装几个辅助插件
npm install --save-dev clean-webpack-plugin html-webpack-plugin webpack webpack-cli
const path = require('path')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugins = require('html-webpack-plugin')

module.exports = {
    // 入口js路径,注意这里是./而不是../,下面HtmlWebpackPlugins的template属性也一样,我理解的是这里的路径都是相对于项目根目录
    entry: './src/main.js',
    // 配置编译文件输出路径
    output: {
        // 以原始文件名的输出文件名
        filename: '[name].js',
        path: path.resolve(__dirname, '../dist')
    },
    plugins: [
        // 自动清空dist文件夹
        new CleanWebpackPlugin(),
        // 设置html模板生成路径
        new HtmlWebpackPlugins({
            filename: 'index.html', // 输出的html文件名,默认index.html
            template: './public/index.html', // html模板路径,注意这里也是./而不是../
            chunks: ['main'] // 指定在html自动引入的js打包文件
        })

    ]
}

  1. 修改main.js文件
document.write('hello cvue-cli!')
  1. 运行npm run build打包编译文件,打包完的目录结构如下
打包完的文件

浏览器打开index.html文件,页面中出现 “hello cvue-cli!”,恭喜你成功地迈出了第一步,下面再接再厉!

  1. 下面搭建开发环境的热监测服务器
npm install --save-dev webpack-dev-server
  1. 修改package.json中的dev命令
"dev": "webpack-dev-server --config build/webpack.base.config.js --open --mode development",

9.修改webpack.base.config.js文件

//  添加如下内容
devServer: {
   contentBase: './dist', //指定需要提供给本地服务的内容的路径,默认加载index.html文件,可根据需要修改
   port: 8080, // 端口设置,默认8080
   hot: true // 开启热更新,浏览器自动刷新
}
  1. 将webpack配置进行分离,分为本地开发环境和生产环境
// 首先安装插件
npm install --save-dev webpack-merge

webpack.base.config.js保留如下内容(注意:这里修改了js的打包输出路径):

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

module.exports = {
    // 入口js路径,注意这里是./而不是../,下面HtmlWebpackPlugins的template属性也一样,我理解的是这里的路径都是相对于项目根目录
    entry: './src/main.js',
    // 配置编译文件输出路径
    output: {
        // 以原始文件名的输出文件名
        filename: 'js/[name].js',
        path: path.resolve(__dirname, '../dist')
    },
    plugins: [
        // 自动清空dist文件夹
        new CleanWebpackPlugin(),
        // 设置html模板生成路径
        new HtmlWebpackPlugins({
            filename: 'index.html', // 输出的html文件名,默认index.html
            template: './public/index.html', // html模板路径,注意这里也是./而不是../
            chunks: ['main'] // 指定在html自动引入的js打包文件
        })
    ]
}

webpack.dev.config.js修改为如下内容:

const merge = require('webpack-merge')
const baseConfig = require('./webpack.base.config')

module.exports = merge(baseConfig, {
    devServer: {
        contentBase: './dist', //指定需要提供给本地服务的内容的路径,默认加载index.html文件,可根据需要修改
        port: 8080, // 端口设置,默认8080
        hot: true // 开启热更新,浏览器自动刷新
    }
})

webpack.prod.config.js修改为如下内容:

const merge = require('webpack-merge')
const baseConfig = require('./webpack.base.config')

module.exports = merge(baseConfig, {
    // 方便追踪源代码错误
    devtool: 'source-map'
})

然后,我们修改下package.json中的dev和build命令:

"scripts": {
    "dev": "webpack-dev-server --config build/webpack.dev.config.js --open --mode development",
    "build": "webpack --config build/webpack.prod.config.js --mode production"
  },

最后,尝试运行下npm run dev 和 npm run build,看运行结果是否正常!

搭建cvue-cli v2.0

  1. 首先安装相关依赖包(每个具体的作用就不介绍了,可自行百度)
npm install --save vue
npm install --save-dev vue-loader vue-template-compiler vue-style-loader css-loader
  1. 修改webpack.base.config.js文件,添加相应loader的配置规则,以及在plugins中添加vue-loader官方提供的魔法插件
module: {
     rules: [
         {
             test: /\.vue$/,
             use: ['vue-loader']
         },
         // 它会应用到普通的 `.css` 文件以及 `.vue` 文件中的 `<style>` 块,因为魔法插件VueLoaderPlugin
         {
             test: /\.css$/,
             use: ['vue-style-loader', 'css-loader']
         }
    ]
}
const VueLoaderPlugin = require('vue-loader/lib/plugin')

plugins: [
    // 将定义过的其它规则复制并应用到 .vue 文件里相应语言的块
    new VueLoaderPlugin()
]
  1. 添加App.vue文件


    App.vue
<template>
  <div id="app"></div>
</template>

<script>
export default {
  data() {
    return {
      message: 'hello cvue-cli'
    }
  }
}
</script>

<style>
#app {
  color: red;
}
</style>

  1. 下面修改main.js文件,引入Vue的使用
import Vue from 'vue'
import App from './APP.vue'

new Vue({
  el: '#app',
  render: h => h(App)
})

注意

new Vue({
  el: '#app',
  components: { App },
  template: '<App/>'
})

如果在创建根实例时,采用上面的写法,浏览器会提示下面的错误:

浏览器错误

引起该错误的原因和解决方法请查看下面的链接:
https://blog.csdn.net/wxl1555/article/details/83187647

  1. 修改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>cvue-cli</title>
</head>
<body>
    <div id="app"></div>
</body>
</html>

然后执行npm run dev,如果页面中出现了message对应的值,且字体为红色,说明vue单文件起作用了!

  1. 下面安装file-loader和url-loader
// 因为url-loader基于file-loader,所以都要安装。
// url-loader在file-loader上进行扩展,当文件小于多少KB时进行base64转换
npm install --save-dev file-loader url-loader
  1. 在webpack.base.config.js中添加文件loader的规则
// 图片文件处理
{
    test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
    use: {
         loader: 'url-loader',
         options: {
              limit: 10000
         }
    }
},
// 音频文件
{
    test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
    use: {
         loader: 'url-loader',
         options: {
              limit: 10000
         }
    }
},
// 字体文件
{
     test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
     use: {
          loader: 'url-loader',
          options: {
               limit: 10000
          }
     }
}
  1. 在src目录下创建assets文件夹,用来存放我们的图片(这里使用vue官方logo),然后修改App.vue文件


    assets文件夹
<template>
  <div id="app">
    <img src="./assets/logo.png">
    <h1>{{message}}</h1>
  </div>
</template>

<script>
export default {
  name: 'App',
  data() {
    return {
      message: 'hello cvue-cli'
    }
  }
}
</script>

<style>
#app {
  text-align: center;
}
</style>

最后运行 npm run dev,如果浏览器中出现如下的样子,那么恭喜你,马上就可以完成cvue-cli v2.0的搭建了。怎么样?心里是不是有点小激动呢?别急,下面我们需要继续完善。


初级版cvue-cli v2.0
  1. 下面安装和使用vue-router

安装vue-router

npm install --save vue-router

添加相关文件,项目结构如下


添加使用vue-router相关文件

hello-router.vue文件

<style>
.wrapper > h1 {
  color: blue;
}
</style>

<template>
  <div class="wrapper">
    <h1>{{mesg}}</h1>
  </div>
</template>

<script>
export default {
  data() {
    return {
      mesg: "hello router"
    }
  }
}
</script>

index.js文件

import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

const routes = [{
  path: '/',
  name: 'hello-router',
  component: () => import('../components/hello-router.vue')
}]

const router = new Router({
  routes
})

export default router

main.js文件

import Vue from 'vue'
import App from './APP.vue'
import router from './router/index'

new Vue({
  el: '#app',
  router,
  render: h => h(App)
})

然后运行npm run dev,出现下图:


成功使用vue-router
  1. 安装使用css预处理器-less

安装less和less-loader

npm install --save-dev less less-loader

在webpack.base.config.js中配置less-loader

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

添加hello-router.less文件


添加hello-router.less
.wrapper {
  h2 {
    color: green;
  }
}

修改hello-router.vue

<style lang="less">
@import './hello-router.less';
.wrapper {
  h1 {
    color: purple;
  }
}
</style>

<template>
  <div class="wrapper">
    <h1>{{mesg}}</h1>
    <h2>{{mesg2}}</h2>
  </div>
</template>

<script>
export default {
  data() {
    return {
      mesg: 'hello router',
      mesg2: 'i am mesg2!'
    }
  }
}
</script>

运行npm run dev,效果如下


less生效的效果图

下面我们在运行npm run build,然后在看下本地打包后的文件是否有问题。
至此,cvue-cli v2.0搭建完毕!

搭建cvue-cli v3.0

在cvue-cli v3.0中,我们需要将打包后的文件js和css该分离的分离,该压缩的压缩!

1.分离css样式
安装mini-css-extract-plugin插件

npm install --save-dev mini-css-extract-plugin

然后在webpack.base.config.js中引入插件,并进行配置

const MiniCssExtractPlugin = require('mini-css-extract-plugin')

// 将vue-style-loader替换为 MiniCssExtractPlugin.loader
{
  test: /\.css$/,
  use: [MiniCssExtractPlugin.loader, 'css-loader']
},
{
  test: /\.less$/,
  use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader']
}

new MiniCssExtractPlugin({
  // 配置分离样式文件名,还有publicPath属性可根据需要进行配置
  filename: 'css/[name].css'
})

运行npm run build,可查看分离出的css文件


css样式分离文件

2.使用ejs将html模板化

安装ejs-loader

npm install --save-dev ejs-loader

修改webpack.base.config.js,支持ejs

// 添加loader规则
{
  test: /\.ejs$/,
  use: ['ejs-loader']
}
// 修改插件配置
new HtmlWebpackPlugins({
  filename: 'index.html', // 输出的html文件名,默认index.html
  template: './public/index.ejs', // html模板路径,注意这里也是./而不是../
  chunks: ['main'] // 指定在html自动引入的js打包文件
})
  1. 下面设置下文件缓存
    在输出chunk时,我们可以通过hash来设置文件名。webpack提供了三种hash方式,分别是hash,chunkhash,contenthash。
  • hasn
    hash是项目工程级的,整个工程构建的文件hash都是一样的,所以只要工程文件有一个修改了,那么所有打包文件的hash都会改变,这明显不利于文件缓存,比如第三方库的chunk。
  • chunkhash
    chunkhash和hash不一样,它根据不同的入口文件(Entry)进行依赖文件解析、构建对应的chunk,生成对应的hash值。我们在生产环境里把一些公共库和程序入口文件区分开,单独打包构建,接着我们采用chunkhash的方式生成hash值,那么只要我们不改动公共库的代码,就可以保证其hash值不会受影响。
  • contenthash
    contenthash表示由文件内容产生的hash值,内容不同产生的contenthash值也不一样。在项目中,通常做法是把项目中css都抽离出对应的css文件来加以引用。

下面修改webpack.base.config.js

// 配置编译文件输出路径
output: {
  // 以原始文件名的输出文件名
  filename: 'js/[name].[chunkhash].js',
  path: path.resolve(__dirname, '../dist')
}
// 配置编译文件输出路径
new MiniCssExtractPlugin({
  filename: 'css/[name].[contenthash].css'
})

修改HtmlWebpackPlugins插件的配置

// 设置html模板生成路径
new HtmlWebpackPlugins({
  filename: 'index.html', // 输出的html文件名,默认index.html
  template: './public/index.ejs', // html模板路径,注意这里也是./而不是../
  chunks: ['main', 'vendor'], // 指定在html自动引入的js打包文件
})

然后大家可以分别试一下,加上chunkhash和contenthash有什么区别,是不是和上面介绍的那样。
最后运行npm run buil打包,打开index.html,看页面是否显示正常。

  1. 热更新(HMR)不能和[chunkhash]同时使用
    按照上面的步骤,在运行打包文件时,页面在时可以正常显示的。但是如果运行了npm run dev,就会报如下错误:


    错误

    这是因为热更新(HMR)不能和[chunkhash]同时使用。那么怎么办呢?好说,我们不是把配置分为了本地环境和生产环境吗?那么我们就在使用本地环境时不使用hash方式命名就可以啦!
    修改webpack.dev.config.js 文件

const merge = require('webpack-merge')
const baseConfig = require('./webpack.base.config')
const path = require('path')

module.exports = merge(baseConfig, {
  // 配置编译文件输出路径
  output: {
    // 以原始文件名的输出文件名
    filename: 'js/[name].js',
    path: path.resolve(__dirname, '../dist')
  },
  devServer: {
    contentBase: './dist', //指定需要提供给本地服务的内容的路径,默认加载index.html文件,可根据需要修改
    port: 8080, // 端口设置,默认8080
    hot: true // 开启热更新,浏览器自动刷新
  }
})
  1. 将项目的引入的图片等文件进行分离
    修改webpack.base.config.js
// 图片文件处理
{
  test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
  use: {
    loader: 'url-loader',
    options: {
      limit: 0, // 这设为0,因为如果文件大小小于limit设置的值,会直接转为base64
      outputPath: 'images'
    }
  }
}

运行 npm run build,在打包后的项目目录结构中会多出一个images文件夹。其他文件的分离方式也类似,如果需要可以自行配置。

结语

按照上面方式,就可以搭建出一个基本的仿vue-cli的脚手架。建议大家还是亲自去试一试,这样可以加深理解。

最后在附上本项目的git地址,大家可以下来看看:
cvue-cli的git地址

好啦,本篇文章内容到此结束,如果对上面的内容有什么不同的理解或是发现了错误,欢迎大家来交流指正。
感谢收看! \o(^o^)o

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

推荐阅读更多精彩内容