优秀的编程知识分享平台

网站首页 > 技术文章 正文

前端构建:采用@vue/cli方式工程化项目,如何在webpack配置?

nanyue 2024-07-20 23:49:08 技术文章 11 ℃

链接https://juejin.im/post/5d130b9a518825670124a721?utm_source=bigezhang.com#comment

vue-cil 3.0 配置说明

1 安装vue-cli3.0

0 淘宝镜像

npm install -g cnpm --registry=https://registry.npm.taobao.org

1 安装vue-cli

npm install -g @vue/cli
# OR
yarn global add @vue/cli

2 查看版本

vue --version

3 vue-cli构建项目

vue create vue-demo

选择默认即可、开启服务

4 目录结构

├── README.md 					# 说明
|-- dist 	# 打包后文件夹
├── babel.config.js 			# babel语法编译
├── package-lock.json 
├── package.json
├── public						# 静态文件夹
│ ├── favicon.ico
│ └── index.html				#入口页面
└── src						 # 源码目录
 ├── App.vue - 页面
 ├── assets - 静态目录
 │ └── logo.png
 ├── components 组件
 │ └── HelloWorld.vue
 └── main.js # 入口文件,加载公共组件
|-- vue.config.js # 配置文件 
|-- .eslintrc.js 		 	# ES-lint校验 
|-- .gitignore 		# git忽略上传的文件格式 
|-- babel.config.js 			# babel语法编译 
|-- package.json 	 # 项目基本信息 
复制代码

标准的vue目录结构

2 环境变量和模式

0 介绍

在npm下实际上vue-cli 启动的时候,已经确定了环境变量与模式,这样方便代码的构建

打开package.json后

我们这边描述的环境就在scripts中

1 模式

是 Vue CLI 项目中一个重要的概念。默认情况下,一个 Vue CLI 项目有三个模式:

  • development 模式用于 vue-cli-service serve
  • production 模式用于 vue-cli-service build 和 vue-cli-service test:e2e
  • test 模式用于 vue-cli-service test:unit

你可以通过传递 --mode 选项参数为命令行覆写默认的模式。例如,如果你想要在构建命令中使用开发环境变量,请在你的 package.json 脚本中加入

```bash "dev-build": "vue-cli-service build --mode development", ```

2 调用

那么我们在代码里面怎么调用呢、和他的应用场景呢

process.env.NODE_ENV

这样我们就可以获取他的环境变量

3 场景

我们在src目录下新建一个config

构建一个env.js的目录

let baseUrl = '';
const env = process.env
if (env.NODE_ENV == 'development') {
 baseUrl = `http://192.168.1.1`; // 开发环境地址
} else if (env.NODE_ENV == 'production') {
 baseUrl = `http://192.168.1.2`; //生产环境地址
} else if (env.NODE_ENV == 'test') {
 baseUrl = `http://192.168.1.3`; //测试环境地址
}
export {
 baseUrl,
 env
}

那么这样我们就可以直接使用不同环境下的

3 IE兼容处理、移除console

npm install @babel/polyfill -s
npm install babel-plugin-transform-remove-console -s

在babel.config.js中配置如下

const plugins = []
if (process.env.NODE_ENV === 'production') {
 // 移除console.log
 plugins.push('transform-remove-console')
}
module.exports = {
 presets: [
 ['@vue/app', {
 polyfills: [
 'es6.array.iterator',
 'es6.promise',
 'es7.promise.finally',
 'es6.symbol',
 'es6.array.find-index',
 'es7.array.includes',
 'es6.string.includes',
 'es6.array.find',
 'es6.object.assign'
 ]
 }]
 ],
 plugins
}

4 vue-cli 基本配置

新建一个vue.config.js的文件夹,开始可配置vue-cli

module.exports = {
 //部署应用包时的基本 URL
 publicPath: process.env.NODE_ENV === 'production' ? '/online/' : './',
 //当运行 vue-cli-service build 时生成的生产环境构建文件的目录
 outputDir: 'dist',
 //放置生成的静态资源 (js、css、img、fonts) 的 (相对于 outputDir 的) 目录
 assetsDir: 'assets',
 // eslint-loader 是否在保存的时候检查 安装@vue/cli-plugin-eslint有效
 lintOnSave: true,
 //是否使用包含运行时编译器的 Vue 构建版本。设置true后你就可以在使用template
 runtimeCompiler: true,
 // 生产环境是否生成 sourceMap 文件 sourceMap的详解请看末尾 
 productionSourceMap: false,
}

5 添加别名

新建一个vue.config.js的文件夹,开始可配置vue-cli

1 别名配置

const path = require('path'); //引入path模块(node)
const resolve = (dir) => path.join(__dirname, dir); //将文件组成绝对路径
 
module.exports = {
 chainWebpack: config => {
 // 添加别名
 config.resolve.alias
 .set('@', resolve('src'))
 .set('assets', resolve('src/assets'))
 .set('components', resolve('src/components'))
 }
}

2 运用场景

目录的结构可能层层叠叠,后在达到目录

例如: 刚刚的env.js目录

import {baseUrl} from '../config/env'

但是我们不一定保证,目录就那么靠近,那么怎么处理

// @代表src目录下,即可这样调用
import {baseUrl} from '@/config/env'

6 优化-配置externals

防止将某些 import 的包(package)打包到 bundle 中,而是在运行时(runtime)再去从外部获取这些扩展依赖

### 项目中的使用 > 一般性vue项目,我们都会把一些框架包,给抽离出来。例如:

  • Vue
  • ELEMENT
  • VueRouter
  • Vuex
  • axios

1 引入框架

我们把一些外包引用的包,提取出来,放在public中

2 编辑 externals

我们在vue.config.

module.exports = {
 configureWebpack: config => {
 config.externals = {
 'vue': 'Vue',
 'element-ui': 'ELEMENT',
 'vue-router': 'VueRouter',
 'vuex': 'Vuex',
 'axios': 'axios'
 }
 }

3 引入cdn

那么这时候我们需要在 public/index.html

<!DOCTYPE html>
<html lang="en">
 <head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
 <meta http-equiv="X-UA-Compatible" content="IE=edge">
 <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0">
 <meta name="apple-mobile-web-app-capable" content="yes">
 <meta name="apple-mobile-web-app-status-bar-style" content="black">
 <meta name="format-detection" content="telephone=no">
 <meta http-equiv="X-UA-Compatible" content="chrome=1" />
 <link rel="icon" href="<%= BASE_URL %>favicon.ico">
 <link rel="stylesheet" href="<%= BASE_URL %>cdn/element-ui/2.5.4/theme-chalk/index.css">
 <link rel="stylesheet" href="<%= BASE_URL %>cdn/animate/3.5.2/animate.css">
 <link rel="stylesheet" href="<%= BASE_URL %>cdn/iconfont/1.0.0/index.css">
 <link rel="stylesheet" href="<%= BASE_URL %>cdn/iconfont/1.0.0/iconfont.css">
 <link rel="stylesheet" href="<%= BASE_URL %>cdn/iconfont/1.0.0/index.css">
 <title>vue-demo</title>
 </head>
 <body>
 <noscript>
 <strong>We're sorry but vue-demo doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
 </noscript>
 <div id="app"></div>
 <!-- built files will be auto injected -->
 <script src="<%= BASE_URL %>cdn/vue/2.5.2/vue.min.js" charset="utf-8"></script>
 <script src="<%= BASE_URL %>cdn/vuex/2.4.1/vuex.min.js" charset="utf-8"></script>
 <script src="<%= BASE_URL %>cdn/vue-router/3.0.1/vue-router.min.js" charset="utf-8"></script>
 <script src="<%= BASE_URL %>cdn/axios/1.0.0/axios.min.js" charset="utf-8"></script>
 <script src="<%= BASE_URL %>cdn/element-ui/2.5.4/index.js" charset="utf-8"></script>
 </body>
</html>

7 优化-开启Gzip 压缩

1 介绍

vue cli 3.0相比2.0有不少的改动,最明显的就是 build文件夹不见了,改为根目录的vue.config.js

在此记录一下Gzip配置的过程

npm i -D compression-webpack-plugin
复制代码

2 修改vue.config.js

const CompressionPlugin = require("compression-webpack-plugin")
module.exports = {
	configureWebpack:config=>{
 if(process.env.NODE_ENV === 'production'){
 return{
 plugins: [
 new CompressionPlugin({
 test:/\.js$|\.html$|.\css/, //匹配文件名
 threshold: 10240,//对超过10k的数据压缩
 deleteOriginalAssets: false //不删除源文件
 })
 ]
 }
 }
 },
}

3 nginx配置gzip

gzip on;
gzip_min_length 1k;
gzip_buffers 4 16k;
gzip_http_version 1.0;
gzip_comp_level 6;
gzip_types text/plain application/javascript application/x-javascript text/javascript text/xml text/css;
gzip_disable "MSIE [1-6]\.";
gzip_vary on;

第1行:开启Gzip

第2行:不压缩临界值,大于1K的才压缩,一般不用改

第3行:buffer,就是,嗯,算了不解释了,不用改

第4行:用了反向代理的话,末端通信是HTTP/1.0,有需求的应该也不用看我这科普文了;有这句的话注释了就行> 了,默认是HTTP/1.1

第5行:压缩级别,1-10,数字越大压缩的越好,时间也越长,看心情随便改吧

第6行:进行压缩的文件类型,缺啥补啥就行了,JavaScript有两种写法,最好都写上吧,总有人抱怨js文件没有压> 缩,其实多写一种格式就行了

第7行:跟Squid等缓存服务有关,on的话会在Header里增加"Vary: Accept-Encoding",我不需要这玩意,自己> 对照情况看着办吧

第8行:IE6对Gzip不怎么友好,不给它Gzip了

8 优化-首屏加载

<!DOCTYPE html>
<html lang="en">
 <head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
 <meta http-equiv="X-UA-Compatible" content="IE=edge">
 <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0">
 <meta name="apple-mobile-web-app-capable" content="yes">
 <meta name="apple-mobile-web-app-status-bar-style" content="black">
 <meta name="format-detection" content="telephone=no">
 <meta http-equiv="X-UA-Compatible" content="chrome=1" />
 <link rel="icon" href="<%= BASE_URL %>favicon.ico">
 <link rel="stylesheet" href="<%= BASE_URL %>cdn/element-ui/2.5.4/theme-chalk/index.css">
 <link rel="stylesheet" href="<%= BASE_URL %>cdn/animate/3.5.2/animate.css">
 <link rel="stylesheet" href="<%= BASE_URL %>cdn/iconfont/1.0.0/index.css">
 <link rel="stylesheet" href="<%= BASE_URL %>cdn/iconfont/1.0.0/iconfont.css">
 <link rel="stylesheet" href="<%= BASE_URL %>cdn/iconfont/1.0.0/index.css">
 <title>vue-demo</title>
 
 <style>
 html,
 body,
 #app {
 height: 100%;
 margin: 0px;
 padding: 0px;
 }
 .chromeframe {
 margin: 0.2em 0;
 background: #ccc;
 color: #000;
 padding: 0.2em 0;
 }
 #loader-wrapper {
 position: fixed;
 top: 0;
 left: 0;
 width: 100%;
 height: 100%;
 z-index: 999999;
 }
 #loader {
 display: block;
 position: relative;
 left: 50%;
 top: 50%;
 width: 150px;
 height: 150px;
 margin: -75px 0 0 -75px;
 border-radius: 50%;
 border: 3px solid transparent;
 /* COLOR 1 */
 border-top-color: #FFF;
 -webkit-animation: spin 2s linear infinite;
 /* Chrome, Opera 15+, Safari 5+ */
 -ms-animation: spin 2s linear infinite;
 /* Chrome, Opera 15+, Safari 5+ */
 -moz-animation: spin 2s linear infinite;
 /* Chrome, Opera 15+, Safari 5+ */
 -o-animation: spin 2s linear infinite;
 /* Chrome, Opera 15+, Safari 5+ */
 animation: spin 2s linear infinite;
 /* Chrome, Firefox 16+, IE 10+, Opera */
 z-index: 1001;
 }
 #loader:before {
 content: "";
 position: absolute;
 top: 5px;
 left: 5px;
 right: 5px;
 bottom: 5px;
 border-radius: 50%;
 border: 3px solid transparent;
 /* COLOR 2 */
 border-top-color: #FFF;
 -webkit-animation: spin 3s linear infinite;
 /* Chrome, Opera 15+, Safari 5+ */
 -moz-animation: spin 3s linear infinite;
 /* Chrome, Opera 15+, Safari 5+ */
 -o-animation: spin 3s linear infinite;
 /* Chrome, Opera 15+, Safari 5+ */
 -ms-animation: spin 3s linear infinite;
 /* Chrome, Opera 15+, Safari 5+ */
 animation: spin 3s linear infinite;
 /* Chrome, Firefox 16+, IE 10+, Opera */
 }
 #loader:after {
 content: "";
 position: absolute;
 top: 15px;
 left: 15px;
 right: 15px;
 bottom: 15px;
 border-radius: 50%;
 border: 3px solid transparent;
 border-top-color: #FFF;
 /* COLOR 3 */
 -moz-animation: spin 1.5s linear infinite;
 /* Chrome, Opera 15+, Safari 5+ */
 -o-animation: spin 1.5s linear infinite;
 /* Chrome, Opera 15+, Safari 5+ */
 -ms-animation: spin 1.5s linear infinite;
 /* Chrome, Opera 15+, Safari 5+ */
 -webkit-animation: spin 1.5s linear infinite;
 /* Chrome, Opera 15+, Safari 5+ */
 animation: spin 1.5s linear infinite;
 /* Chrome, Firefox 16+, IE 10+, Opera */
 }
 @-webkit-keyframes spin {
 0% {
 -webkit-transform: rotate(0deg);
 /* Chrome, Opera 15+, Safari 3.1+ */
 -ms-transform: rotate(0deg);
 /* IE 9 */
 transform: rotate(0deg);
 /* Firefox 16+, IE 10+, Opera */
 }
 100% {
 -webkit-transform: rotate(360deg);
 /* Chrome, Opera 15+, Safari 3.1+ */
 -ms-transform: rotate(360deg);
 /* IE 9 */
 transform: rotate(360deg);
 /* Firefox 16+, IE 10+, Opera */
 }
 }
 @keyframes spin {
 0% {
 -webkit-transform: rotate(0deg);
 /* Chrome, Opera 15+, Safari 3.1+ */
 -ms-transform: rotate(0deg);
 /* IE 9 */
 transform: rotate(0deg);
 /* Firefox 16+, IE 10+, Opera */
 }
 100% {
 -webkit-transform: rotate(360deg);
 /* Chrome, Opera 15+, Safari 3.1+ */
 -ms-transform: rotate(360deg);
 /* IE 9 */
 transform: rotate(360deg);
 /* Firefox 16+, IE 10+, Opera */
 }
 }
 #loader-wrapper .loader-section {
 position: fixed;
 top: 0;
 width: 51%;
 height: 100%;
 background: #7171C6;
 /* Old browsers */
 z-index: 1000;
 -webkit-transform: translateX(0);
 /* Chrome, Opera 15+, Safari 3.1+ */
 -ms-transform: translateX(0);
 /* IE 9 */
 transform: translateX(0);
 /* Firefox 16+, IE 10+, Opera */
 }
 #loader-wrapper .loader-section.section-left {
 left: 0;
 }
 #loader-wrapper .loader-section.section-right {
 right: 0;
 }
 /* Loaded */
 .loaded #loader-wrapper .loader-section.section-left {
 -webkit-transform: translateX(-100%);
 /* Chrome, Opera 15+, Safari 3.1+ */
 -ms-transform: translateX(-100%);
 /* IE 9 */
 transform: translateX(-100%);
 /* Firefox 16+, IE 10+, Opera */
 -webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
 transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
 }
 .loaded #loader-wrapper .loader-section.section-right {
 -webkit-transform: translateX(100%);
 /* Chrome, Opera 15+, Safari 3.1+ */
 -ms-transform: translateX(100%);
 /* IE 9 */
 transform: translateX(100%);
 /* Firefox 16+, IE 10+, Opera */
 -webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
 transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
 }
 .loaded #loader {
 opacity: 0;
 -webkit-transition: all 0.3s ease-out;
 transition: all 0.3s ease-out;
 }
 .loaded #loader-wrapper {
 visibility: hidden;
 -webkit-transform: translateY(-100%);
 /* Chrome, Opera 15+, Safari 3.1+ */
 -ms-transform: translateY(-100%);
 /* IE 9 */
 transform: translateY(-100%);
 /* Firefox 16+, IE 10+, Opera */
 -webkit-transition: all 0.3s 1s ease-out;
 transition: all 0.3s 1s ease-out;
 }
 /* JavaScript Turned Off */
 .no-js #loader-wrapper {
 display: none;
 }
 .no-js h1 {
 color: #222222;
 }
 #loader-wrapper .load_title {
 font-family: 'Open Sans';
 color: #FFF;
 font-size: 19px;
 width: 100%;
 text-align: center;
 z-index: 9999999999999;
 position: absolute;
 top: 60%;
 opacity: 1;
 line-height: 30px;
 }
 #loader-wrapper .load_title span {
 font-weight: normal;
 font-style: italic;
 font-size: 13px;
 color: #FFF;
 opacity: 0.5;
 }
 </style>
 
 </head>
 <body>
 <noscript>
 <strong>We're sorry but vue-demo doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
 </noscript>
 
 <div id="app">
 <div id="loader-wrapper">
 <div id="loader"></div>
 <div class="loader-section section-left"></div>
 <div class="loader-section section-right"></div>
 <div class="load_title">正在加载 vue,请耐心等待
 <br>
 <span>V1.3</span>
 </div>
 </div>
 </div>
 <!-- built files will be auto injected -->
 <script src="<%= BASE_URL %>cdn/vue/2.5.2/vue.min.js" charset="utf-8"></script>
 <script src="<%= BASE_URL %>cdn/vuex/2.4.1/vuex.min.js" charset="utf-8"></script>
 <script src="<%= BASE_URL %>cdn/vue-router/3.0.1/vue-router.min.js" charset="utf-8"></script>
 <script src="<%= BASE_URL %>cdn/axios/1.0.0/axios.min.js" charset="utf-8"></script>
 <script src="<%= BASE_URL %>cdn/element-ui/2.5.4/index.js" charset="utf-8"></script>
 </body>
</html>

9 预处理器 (Sass/Less/Stylus)

# Sass
npm install -D sass-loader node-sass
# Less
npm install -D less-loader less
# Stylus
npm install -D stylus-loader stylus
复制代码
然后你就可以导入相应的文件类型,或在 *.vue 文件中这样来使用:
<style scoped lang="stylus">
.personal
 position relative
 .banner-red
 width 100%
 height 100px
</style>

Tags:

最近发表
标签列表