vue cli3

文档

https://cn.vuejs.org/v2/guide/

https://cli.vuejs.org/zh/guide

新建项目

https://github.com/2384830985...


# 安装 Vue Cli
npm install -g @vue/cli

# 创建一个项目
vue create hello-world

# 创建完成后,可以通过命令打开图形化界面,如下图所示
vue ui
  • vuex-router-sync

vue脚手架自动生成框架的技术分析

VUE启动步骤:public/index.html - main.js - App.vue、router.js
-- main.js将App.vue嵌入public/index.html<div id="app"></div>
-- 根据router.js,渲染App的router-view

开发环境

$ node -v
v10.16.1

$ npm -v
6.9.0

安装vue-cli:npm install -g @vue/cli

$ vue -V
3.10.0

创建工程

vue-cli已经被取代!

$ vue create unified-platform-front-yunying

去掉pwa

删除:<li>pwa</li>

去掉dependencies:"register-service-worker": "^1.6.2"

npm uninstall @vue/cli-plugin-pwa

配置

全局配置:$ vue config

配置参考:https://cli.vuejs.org/zh/config/

vue-cli升级到3.x版本之后,构建出来的项目比较简练,但是没有了webpack的显式配置,如果想要脚手架适用自己的项目,就需要配置vue.config.js。

配置完之后可以通过 vue ui 命令打开 vue-cli3 提供的视图界面:任务 -> inspect 来检查 webpack 的配置,也可以使用 vue inspect 来查看某一项的配置。

你可以列出所有规则和插件的名字:

vue inspect --rules

vue inspect --plugins

有些外部工具可能需要通过一个文件访问解析好的 webpack 配置,比如那些需要提供 webpack 配置路径的 IDE 或 CLI。在这种情况下你可以使用如下路径:
<projectRoot>/node_modules/@vue/cli-service/webpack.config.js

该文件会动态解析并输出 vue-cli-service 命令中使用的相同的 webpack 配置,包括那些来自插件甚至是你自定义的配置。

vue-cli-service

"@vue/cli-service": "^3.10.0"

在一个 Vue CLI 项目中,@vue/cli-service 安装了一个名为 vue-cli-service 的命令。你可以在 npm scripts 中以 vue-cli-service、或者从终端中以 ./node_modules/.bin/vue-cli-service 访问这个命令。

  • vue-cli-service serve

vue-cli-service serve 命令会启动一个开发服务器 (基于 webpack-dev-server) 并附带开箱即用的模块热重载 (Hot-Module-Replacement)。

除了通过命令行参数,你也可以使用 vue.config.js 里的 devServer 字段配置开发服务器。

命令行参数 [entry] 将被指定为唯一入口,而非额外的追加入口。尝试使用 [entry] 覆盖 config.pages 中的 entry 将可能引发错误。

  • vue-cli-service build

vue-cli-service build 会在 dist/ 目录产生一个可用于生产环境的包,带有 JS/CSS/HTML 的压缩,和为更好的缓存而做的自动的 vendor chunk splitting。它的 chunk manifest 会内联在 HTML 里。

  • vue-cli-service inspect

你可以使用 vue-cli-service inspect 来审查一个 Vue CLI 项目的 webpack config。更多细节请查阅审查 webpack config。

缓存和并行处理

cache-loader 会默认为 Vue/Babel/TypeScript 编译开启。文件会缓存在 node_modules/.cache 中——如果你遇到了编译方面的问题,记得先删掉缓存目录之后再试试看。

thread-loader 会在多核 CPU 的机器上为 Babel/TypeScript 转译开启。

配置时无需 Eject

通过 vue create 创建的项目无需额外的配置就已经可以跑起来了。插件的设计也是可以相互共存的,所以绝大多数情况下,你只需要在交互式命令提示中选取需要的功能即可。

浏览器兼容性

browserslist

https://github.com/browsersli...

browserslist 是在不同的前端工具之间共用目标浏览器和 node 版本的配置工具。它主要被以下工具使用:

Autoprefixer
Babel
post-preset-env
eslint-plugin-compat
stylelint-unsupported-browser-features
postcss-normalize

链接:https://juejin.im/post/5b8cff326fb9a019fd1474d6

Polyfill

一个默认的 Vue CLI 项目会使用 @vue/babel-preset-app,它通过 @babel/preset-env 和 browserslist 配置来决定项目需要的 polyfill。

默认情况下,它会把 useBuiltIns: 'usage' 传递给 @babel/preset-env,这样它会根据源代码中出现的语言特性自动检测需要的 polyfill。这确保了最终包里 polyfill 数量的最小化。然而,这也意味着如果其中一个依赖需要特殊的 polyfill,默认情况下 Babel 无法将其检测出来。

现代模式

Vue CLI 提供了一个“现代模式”帮你解决这个问题。以如下命令为生产环境构建:

vue-cli-service build --modern

Vue CLI 会产生两个应用的版本:一个现代版的包,面向支持 ES modules 的现代浏览器,另一个旧版的包,面向不支持的旧浏览器。

最酷的是这里没有特殊的部署要求。其生成的 HTML 文件会自动使用 Phillip Walton 精彩的博文中讨论到的技术:

现代版的包会通过 <script type="module"> 在被支持的浏览器中加载(需要配合始终开启的 CORS 进行加载);它们还会使用 <link rel="modulepreload"> 进行预加载。

旧版的包会通过 <script nomodule> 加载,并会被支持 ES modules 的浏览器忽略。

一个针对 Safari 10 中 <script nomodule> 的修复会被自动注入。

对于一个 Hello World 应用来说,现代版的包已经小了 16%。在生产环境下,现代版的包通常都会表现出显著的解析速度和运算速度,从而改善应用的加载性能。

HTML 和静态资源

index.html

public/index.html 文件是一个会被 html-webpack-plugin 处理的模板。在构建过程中,资源链接会被自动注入。

构建一个多页应用

不是每个应用都需要是一个单页应用。Vue CLI 支持使用vue.config.js 中的 pages 选项构建一个多页面的应用。构建好的应用将会在不同的入口之间高效共享通用的 chunk 以获得最佳的加载性能。

注:https://juejin.im/post/5bd128...

处理静态资源

静态资源可以通过两种方式进行处理:

在 JavaScript 被导入或在 template/CSS 中通过相对路径被引用。这类引用会被 webpack 处理。

放置在 public 目录下或通过绝对路径被引用。这类资源将会直接被拷贝,而不会经过 webpack 的处理。

public 文件夹

任何放置在 public 文件夹的静态资源都会被简单的复制,而不经过 webpack。你需要通过绝对路径来引用它们。

注意我们推荐将资源作为你的模块依赖图的一部分导入,这样它们会通过 webpack 的处理并获得如下好处:

脚本和样式表会被压缩且打包在一起,从而避免额外的网络请求。
文件丢失会直接在编译时报错,而不是到了用户端才产生 404 错误。
最终生成的文件名包含了内容哈希,因此你不必担心浏览器会缓存它们的老版本。

public 目录提供的是一个应急手段,当你通过绝对路径引用它时,留意应用将会部署到哪里。如果你的应用没有部署在域名的根部,那么你需要为你的 URL 配置 publicPath 前缀。

环境变量和模式

环境变量

你可以替换你的项目根目录中的下列文件来指定环境变量:

.env                # 在所有的环境中被载入
.env.local          # 在所有的环境中被载入,但会被 git 忽略,只在本地有效的变量
.env.[mode]         # 只在指定的模式中被载入
.env.[mode].local   # 只在指定的模式中被载入,但会被 git 忽略,只在本地有效的变量

被载入的变量将会对 vue-cli-service 的所有命令、插件和依赖可用。

为一个特定模式准备的环境文件的 (例如 .env.production) 将会比一般的环境文件 (例如 .env) 拥有更高的优先级。

此外,Vue CLI 启动时已经存在的环境变量拥有最高优先级,并不会被 .env 文件覆写。

如果在环境中有默认的 NODE_ENV,你应该移除它或在运行 vue-cli-service 命令的时候明确地设置 NODE_ENV。

模式

默认情况下,一个 Vue CLI 项目有三个模式:

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

注意模式不同于 NODE_ENV,一个模式可以包含多个环境变量。也就是说,每个模式都会将 NODE_ENV 的值设置为模式的名称——比如在 development 模式下 NODE_ENV 的值会被设置为 "development"。

可以通过传递 --mode 选项参数为命令行覆写默认的模式。

在客户端侧代码中使用环境变量

只有以 VUE_APP_ 开头的变量会被 webpack.DefinePlugin 静态嵌入到客户端侧的包中。你可以在应用的代码中这样访问它们:

console.log(process.env.VUE_APP_SECRET)

除了 VUE_APP_* 变量之外,在你的应用代码中始终可用的还有两个特殊的变量:

NODE_ENV - 会是 "development"、"production" 或 "test" 中的一个。具体的值取决于应用运行的模式。
BASE_URL - 会和 vue.config.js 中的 publicPath 选项相符,即你的应用会部署到的基础路径。

所有解析出来的环境变量都可以在 public/index.html 中以 HTML 插值中介绍的方式使用。

你可以在 vue.config.js 文件中计算环境变量。它们仍然需要以 VUE_APP_ 前缀开头。这可以用于版本信息 process.env.VUE_APP_VERSION = require('./package.json').version。

注意:process.env,只能在代码中使用,一旦启动,无法在浏览器中访问!!

构建目标

应用模式是默认的模式。在这个模式中:

index.html 会带有注入的资源和 resource hint
第三方库会被分到一个独立包以便更好的缓存
小于 4kb 的静态资源会被内联在 JavaScript 中
public 中的静态资源会被复制到输出目录中

库的使用

如何在 Vue 项目中使用 echarts

推荐直接引入echarts,如下

安装echarts项目依赖
npm install echarts --save

"dependencies": { "echarts": "^4.5.0" },

VUE文件中引入

import echarts from  'echarts'  require('echarts/theme/macarons') // echarts theme  import resize from  './mixins/resize'  // 我们自己写的,可忽略

echarts4.5.0时,以下不需要

// 引入饼图组件  require('echarts/lib/chart/pie') // 引入柱状图组件  require('echarts/lib/chart/bar') // 引入提示框和title组件  require('echarts/lib/component/tooltip') require('echarts/lib/component/title')

示例如下

<template>
  <div :class="className" : />
</template>

<script>
import echarts from 'echarts'
require('echarts/theme/macarons') // echarts theme
import resize from './mixins/resize'

const animationDuration = 6000

export default {
  mixins: \[resize\],
  props: {
    className: {
      type: String,
      default: 'chart'
    },
    width: {
      type: String,
      default: '100%'
    },
    height: {
      type: String,
      default: '450px'
    },
    chartData: {
      type: Object,
      required: true
    }

  },
  data() {
    return {
      chart: null
    }
  },
  watch: {
    chartData: {
      deep: true,
      handler(val) {
        this.setOptions(val)
      }
    }
  },
  mounted() {
    // nextTick里面的代码会在DOM更新后执行
    this.$nextTick(() => {
      this.initChart()
    })
  },
  beforeDestroy() {
    if (!this.chart) {
      return
    }
    this.chart.dispose()
    this.chart = null
  },
  methods: {
    initChart() {
      // this.$el,指的是当前组件的的元素。是在mounted页面渲染后(nextTick)才会出现的,在created的时候是没有的
      this.chart = echarts.init(this.$el, 'macarons')
      this.setOptions(this.chartData)
    },
    setOptions({ legendData, xAxisData, series } = {}) {
      if(null == xAxisData || xAxisData.length < 1 || null == series || series.length < 1){
        return
      }
      const seriesDefault = \[\]

      for (let i = 0, len = series.length; i < len; i++) {
        const item = Object.assign({}, series\[i\])
        // item.name = item.name
        // item.data = item.data
        item.type = 'bar'
        item.barGap = item.barGap || '0'
        item.barCategoryGap = item.barCategoryGap || '40%'
        item.animationDuration = item.animationDuration || animationDuration
        seriesDefault.push(item)
      }

      this.chart.setOption({
        color: \[
          '#94d2fb', '#bfdfab'
        \],
        tooltip: {
          trigger: 'axis',
          textStyle: {
            fontSize: 14
          }
        },
        toolbox: {
          show: false
        },
        legend: {
          x: 'left',
          data: legendData
        },
        grid: {
          bottom: 90
        },
        dataZoom: \[{
            type: 'inside'
        }, {
            type: 'slider'
        }\],
        xAxis: \[
          {
            type: 'category',
            axisLabel: {
              rotate: 30,
              interval: 0
            },
            axisLine: { show: false },
            splitLine: { show: false },
            data: xAxisData
          }
        \],
        yAxis: \[
          {
            type: 'value',
            name: '',
            axisLabel: {
              formatter: '{value}'
            },
            axisLine: { show: false },
            splitNumber: 8
          }
        \],
        series: seriesDefault
      })

部署

通用指南

如果你用 Vue CLI 处理静态资源并和后端框架一起作为部署的一部分,那么你需要的仅仅是确保 Vue CLI 生成的构建文件在正确的位置,并遵循后端框架的发布方式即可。
-- 即本项目采用的方式,后端是unified-platform/web

如果你独立于后端部署前端应用——也就是说后端暴露一个前端可访问的 API,然后前端实际上是纯静态应用。那么你可以将 dist 目录里构建的内容部署到任何静态文件服务器中,但要确保正确的 publicPath。

本地预览

dist 目录需要启动一个 HTTP 服务器来访问 (除非你已经将 publicPath 配置为了一个相对的值),所以以 file:// 协议直接打开 dist/index.html 是不会工作的。

history 模式

vue-router 默认 hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载。

如果不想要很丑的 hash,我们可以用路由的 history 模式,这种模式充分利用 history.pushState API 来完成 URL 跳转而无须重新加载页面。

当你使用 history 模式时,URL 就像正常的 url,例如 http://yoursite.com/user/id,也好看!

所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。

  • nginx

location / {
  try_files $uri $uri/ /index.html;
}

注:若是tomcat、jetty,则需要在WEB-INF目录下的web.xml文件中添加如下<error-code>404</error-code>,以便在vue的index.html中重新路由(router在这里设置了路由跳转)

<error-page>
    <error-code>404</error-code>
    <location>/index.html</location>
</error-page>

https://segmentfault.com/a/1190000010379441

注:我们本质是单页面(多个单页面),需要支持/giftpack/index的形式直接url访问,故也有vue-router问题。
ANS:单独npm run serve启动前端,是没有问题,可直接访问http://localhost:9092/unified-platform-web/yunying/giftpack/index,
但是,若和后端框架一起作为部署的一部分,如resin、jetty中,则无法直接访问http://localhost:8092/unified-platform-web/yunying/giftpack/index!!

axios

在main.js中如下声明使用

import axios from 'axios';
Vue.prototype.$axios=axios;

那么在其他vue组件中就可以this.$axios调用使用

http request 请求拦截器

axios.interceptors.request.use(
    config => {
        if (token) {  // 每次发送请求之前判断是否存在token,如果存在,则统一在http请求的header都加上token,不用每次请求都手动添加了
            config.headers.Authorization = token;
        }
        return config;
    },
    err => {
        return Promise.reject(err);
    }
);

设置默认参数:
this.$axios.defaults.headers.common['sign'] = sign;

catch也会捕获then中的异常!

修改contextPath

1.修改vue.config.js
publicPath: '/unified-platform-web/front_yunying/' -- 默认是‘/’。 静态文件,都会自动寻址...${publicPath}/123.css。

2.修改router
vue-router配置base的路径,这个路径默认是‘/’,把配置改成base: '/unified-platform-web/front_yunying'
-- 可直接访问路由:http://localhost:9092/unified-platform-web/front_yunying/giftpack/all

将dist下生成的文件,复制到后端webapp/front_yunying中(后端的contextpath=/unified-platform-web)

注:devServer.proxy,需要特殊处理,避免本地serve启动时静态文件也指向代理地址

vue-router

https://router.vuejs.org/zh/

获取url参数:var sign = this.$route.query.sign;

获取路由参数:$route.params.id

点击 <router-link :to="..."> 等同于调用 router.push(...)

在 Vue 实例内部,你可以通过 $router 访问路由实例。

对于同一个路由User组件,不同路由参数,例如从 /user/foo 导航到 /user/bar,原来的组件实例会被复用。因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效。不过,这也意味着组件的生命周期钩子不会再被调用。
复用User组件时,想对路由参数的变化作出响应的话,你可以简单地在User中 watch (监测变化) $route 对象,或者使用 2.2 中引入的 beforeRouteUpdate 导航守卫。

当使用一个通配符时,$route.params 内会自动添加一个名为 pathMatch 参数。

<router-view> 是最顶层的出口,渲染最高级路由匹配到的组件。同样地,一个被渲染组件同样可以包含自己的嵌套 <router-view>

没有匹配到合适的子路由,则子路由占位符不会渲染任何东西。如果你想要渲染点什么,可以提供一个 空的 子路由。

router.go(n) - 这个方法的参数是一个整数,意思是在 history 记录中向前或者后退多少步,类似 window.history.go(n)

<router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link> 等价于router.push({ name: 'user', params: { userId: 123 }})

可以在界面中拥有多个单独命名的视图,而不是只有一个单独的出口。如果 router-view 没有设置名字,那么默认为 default。如`<router-view class="view three" name="b"></router-view>

一个视图使用一个组件渲染,因此对于同个路由,多个视图就需要多个组件。

const router = new VueRouter({
  routes: [
    {
      path: '/',
      components: {
        default: Foo,
        a: Bar,
        b: Baz
      }
    }
  ]
})

完整的导航解析流程

导航被触发。
在失活的组件里调用离开守卫。
调用全局的 beforeEach 守卫。
在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
在路由配置里调用 beforeEnter。
解析异步路由组件。
在被激活的组件里调用 beforeRouteEnter。
调用全局的 beforeResolve 守卫 (2.5+)。
导航被确认。
调用全局的 afterEach 钩子。
触发 DOM 更新。
用创建好的实例调用 beforeRouteEnter 守卫中传给 next 的回调函数。

Vue2.0的三种常用传值方式、父传子、子传父、非父子组件传值

https://blog.csdn.net/lander_...

https://blog.csdn.net/cckevin...

父组件向子组件传值: 使用props属性来定义父组件传递过来的数据

子组件中data和props的区别: 

子组件中的data数据,不是通过父组件传递的是子组件私有的,是可读可写的。

子组件中的所有 props中的数据,都是通过父组件传递给子组件的,是只读的。

子组件向父组件传值: 父组件将方法的引用,传递到子组件内部,子组件在内部调用父组件传递过来的方法,同时把要发送给父组件的数据,在调用方法的时候当作参数传递进去

父组件:<son @func="getMsg"></son>

子组件:this.$emit('func', 'OK'); // 调用父组件传递过来的方法,同时把数据传递出去

非父子组件之间传值,需要定义个公共的公共实例文件bus.js,作为中间仓库来传值,不然路由组件之间达不到传值的效果

//bus.js
import Vue from 'vue'
export default new Vue()


// 组件A
// 引入公共的bug,来做为中间传达的工具
import Bus from './bus.js'
Bus.$emit('val', this.elementValue)

// 组件B
// 引入公共的bug,来做为中间传达的工具
import Bus from './bus.js'
// 用$on事件来接收参数
Bus.$on('val', (data) => {
    console.log(data)
    vm.name = data
})

v-model上绑定一个 vuex 值

使用 computed 的 set 和 get 方法,你也可以把它当做一个 watch的升级版。它可以监听数据的变化,当发生变化时,做一些额外的操作。最经典的用法就是v-model上绑定一个 vuex 值的时候,input 发生变化时,通过 commit更新存在 vuex 里面的值。

多环境开发

rem适配方案

还是那句话,用vw还是用rem,这是个问题?

选用rem的原因是因为vant直接推荐了这个适配方案,直接上手:

Vant 中的样式默认使用px作为单位,如果需要使用rem单位,推荐使用以下两个工具

  • postcss-pxtorem 是一款 postcss 插件,用于将单位转化为 rem

也可用:postcss-px2rem-exclude

  • lib-flexible 用于设置 rem 基准值

下面提供了一份基本的 postcss 配置,可以在此配置的基础上根据项目需求进行修改

module.exports = {
  plugins: {
    'autoprefixer': {
      browsers: ['Android >= 4.0', 'iOS >= 7']
    },
    'postcss-pxtorem': {
      rootValue: 37.5,
      propList: ['*']
    }
  }
}

移动端console.log

  • vconsole(在移动端查看调试器)

常见运行依赖

  • qs
  • lodash
  • moment
  • vue-router(路由跳转)
  • axios(数据请求)
  • vuex(状态管理)
  • vue-awesome-picker
  • vue-awesome-swiper
  • vue-lazyload(图片懒加载)
  • fastclick(解决移动端浏览器 300 毫秒点击延迟问题)
  • vue-touch(手势判断)

常见开发依赖

  • @vue/cli-plugin-eslint
  • @vue/cli-plugin-babel
  • @vue/cli-plugin-pwa
  • @vue/cli-service
  • @vue/eslint-config-standard
  • http-push-webpack-plugin
  • compression-webpack-plugin
  • html-webpack-inline-source-plugin
  • px2rem-loader
  • cross-env
  • mock.js(模拟后台数据)

生产环境使用CDN

优化方案

  • 腾讯智图(压缩图片,减少图片的体积)
  • vue-lazyload(图片懒加载,缓解加载数据,提高网页性能)
  • fastclick(解决移动端300ms延迟,提高页面交互流畅度)
  • vue-rouer(路由懒加载,分离app的js为多个js文件,到对应的页面再执行对应的js)
  • webpack(config/index.js文件内的productionSourceMap改为false,这样打包出来的文件可以没有.map结尾的js文件,且文件体积减少至少一半)
  • Vuex刷新保存状态

使用Vuex做状态管理的时候,当用户刷新页面,Vuex里面的状态会全部丢失,从而引起程序的一场。解决思路是在creared()钩子函数里面添加以下方法:

created(){
   console.log('页面执行刷新时,保存Vuex的状态到LocalStorage')
    //在页面加载时读取localStorage里的状态信息
    localStorage.getItem("userMsg") && this.$store.replaceState(Object.assign(this.$store.state,JSON.parse(localStorage.getItem("userMsg"))));
    
    //在页面刷新时将vuex里的信息保存到localStorage里
    window.addEventListener("beforeunload",()=>{
        localStorage.setItem("userMsg",JSON.stringify(this.$store.state))
    })
  }  

上面代码的原理是,当页面刷新时,会将当前Vuex的状态存储到LocalStorage里面,刷新成功,再从LocalStorage赋值到Vuex里面.

pwa

https://github.com/JXtreehous...
https://zhuanlan.zhihu.com/p/...

错误监控

持续集成服务 Travis CI/ gitlab-ci

项目升级vue cli3

「Vue实践」项目升级vue-cli3的正确姿势

vue-cli3 项目从搭建优化到docker部署

vue-cli3 项目从搭建优化到docker部署

通过vue-cli3构建一个SSR应用程序

通过vue-cli3构建一个SSR应用程序

常见问题

关于tunneling socket could not be established , cause=getaddrinfo ENOTFOUND 错误的解决方法

初始化一个vue项目的时候,出现了一些问题
图片描述
图片描述
因为我的代理设置的是这个:
图片描述

团队建设

生鲜 B2B 技术平台的前端团队该如何搭建
技术栈:小菜前端的技术栈是如何规划和演进的
技术栈:为什么 Node 是小菜前端团队的核心技术栈

架构

大型项目前端架构浅谈
Vue 项目架构设计与工程化实践
前端工程师必备:前端的模块化
用 Feature First 的方式管理前端项目复杂度

参考

封装Vue组件的一些技巧
基于vue-cli3.0构建功能完善的移动端架子
「Vue实践」武装你的前端项目
Vue CLI 3结合Lerna进行UI框架设计
技术地图 - vue-cli
一张图教你快速玩转vue-cli3
Vue全家桶商城全站升级之引入HTTPS,PWA,错误监控,持续构建。

阅读 1.8k

推荐阅读
镜心的小树屋
用户专栏

方寸湛然GitHub组织地址:[链接]

47 人关注
123 篇文章
专栏主页