1. vue-cli 构建项目

官网地址

  • 命令行

    1. # 全局安装 vue-cli
    2. $ npm install --global vue-clif
    3. # 创建一个基于 webpack 模板的新项目
    4. $ vue init webpack your-project-name
    5. # 安装依赖,走你
    6. $ npm install
    7. # 进入项目
    8. $ cd your-project-name
    9. # 开发版本打包并运行
    10. $ npm run dev
    11. # 线上环境整个项目打包 生成 dist 可以直接部署到服务器上的文件夹
    12. npm run build

    2. 项目模板中使用 less 方法

    原文地址 vue-cli 构建的项目默认是不支持 less 的,需要自己添加。

  • 首先安装 less 和 less-loader ,在项目目录下运行如下命令

    1. # npm安装
    2. $ npm install less less-loader --save-dev
    3. # 或者使用 yarn
    4. $ yarn add less less-loader --dev
  • 安装成功后,打开 build/webpack.base.conf.js ,在 module.exports = 的对象的 module.rules 后面添加一段:

    1. module.exports = {
    2. // 此处省略无数行,已有的的其他的内容
    3. module: {
    4. rules: [
    5. // 此处省略无数行,已有的的其他的规则
    6. {
    7. test: /\.less$/,
    8. loader: "style-loader!css-loader!less-loader",
    9. }
    10. ]
    11. }
    12. }

    vue-实战各种小技巧(长期更新) - 图1

  • 最后在代码中的 style 标签中 加上 lang=”less” 属性即可

    1. <style scoped lang="less">
    2. </style>

    vue-实战各种小技巧(长期更新) - 图2

  • 之后在项目中测试是否成功

    1. npm install less less-loader --save-dev
    2. npm run dev

    vue-实战各种小技巧(长期更新) - 图3

  • 在浏览其中打开相应页面,这个页面是 / 根页面点击跳转过来的子路由

    vue-实战各种小技巧(长期更新) - 图4 vue-实战各种小技巧(长期更新) - 图5 可以看到样式编译成功了 哦耶~

    3. 在 router 下的路由文件里设置格式,将页面上路由中默认显示的 #/ 给去掉

  1. // 去掉路由中自带的 #/ 这种东西
  2. mode: 'history',

vue-实战各种小技巧(长期更新) - 图6

  • 需要注意的是使用了 history 之后需要在服务器部署时增加一些配置,具体方法插件下面官方写的配置方法

    配置方法

    4. 引入 jquery

  • 安装

    1. npm install jquery --save
  • 配置

    vue-实战各种小技巧(长期更新) - 图7 vue-实战各种小技巧(长期更新) - 图8

  1. // 先在顶部引入 webpack
  2. const webpack = require('webpack')
  3. // plugins 中添加
  4. new webpack.ProvidePlugin({
  5. 'window.jQuery': 'jquery', // 为了兼容其他的插件
  6. jQuery: 'jquery',
  7. $: 'jquery'
  8. })
  • 使用

    vue-实战各种小技巧(长期更新) - 图9

    5. :class 使用表达式

  1. :class="{'想要改变的类名': 判断条件}
  • 示例图片

    vue-实战各种小技巧(长期更新) - 图10

    6. DOM 事件修饰符

  1. <!-- 阻止单击事件继续传播 -->
  2. <a v-on:click.stop="doThis"></a>
  3. <!-- 提交事件不再重载页面 -->
  4. <form v-on:submit.prevent="onSubmit"></form>
  5. <!-- 修饰符可以串联 -->
  6. <a v-on:click.stop.prevent="doThat"></a>
  7. <!-- 只有修饰符 -->
  8. <form v-on:submit.prevent></form>
  9. <!-- 添加事件监听器时使用事件捕获模式 -->
  10. <!-- 即元素自身触发的事件先在此处处理,然后才交由内部元素进行处理 -->
  11. <div v-on:click.capture="doThis">...</div>
  12. <!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
  13. <!-- 即事件不是从内部元素触发的 -->
  14. <div v-on:click.self="doThat">...</div>
  • 示例,如下图所示,这样写的话点击了 li 内部的元素的话不会影响 li 的 click 的点击事件

    vue-实战各种小技巧(长期更新) - 图11

    7. vue 使用 clipboard 实现复制功能

    原文地址

  • 安装依赖 clipboard.js

    1. npm install clipboard --save
  • 在需要使用的地方 require 引用

    1. var clipboard = require('clipboard');
  • 在页面加载后调用该方法即可

    vue-实战各种小技巧(长期更新) - 图12

    8. 解决 vue-resource 的跨越问题

    我这里是 vue-cli 基于 webpack 的项目(注意:在修改了 proxyTable 之后需要在命令行中 npm run dev 重新运行下项目,否则是不会有效果的呀~)

  • 错误信息

    Request header field Content-Type is not allowed by Access-Control-Allow-Headers in preflight response.

  • 解决方法:

    先找到对应的配置 js 文件中的 proxyTable vue-实战各种小技巧(长期更新) - 图13 修改相应的配置 vue-实战各种小技巧(长期更新) - 图14 再在 main.js 中配置 vue-resource 的格式,不配置的话是无法向后台传递参数的 vue-实战各种小技巧(长期更新) - 图15 在 vue 文件中的使用 data 中绑定相应的静态配置 vue-实战各种小技巧(长期更新) - 图16 methods 增加相应的方法 vue-实战各种小技巧(长期更新) - 图17 mouted 在 data 数据挂载到实例对象的时候 ,请求页面数据,实现页面的正常显示 vue-实战各种小技巧(长期更新) - 图18

    9. vue-router 单页之间如何在 js 中跳转

    原文地址

  • 三种写法

    1. // 字符串
    2. this.$router.push('/home/first')
    3. // 对象
    4. this.$router.push({ path: '/home/first' })
    5. // 命名的路由
    6. this.$router.push({ name: 'home', params: { userId: wise }})

    vue-实战各种小技巧(长期更新) - 图19

    10. vuex 实现组件之间数据的传递

    根据 state 可以实时的获取到数据 原文地址

  • 安装

    1. npm install vuex --save
  • 在 src 文件夹中新建一个 stroe 文件夹,并在目录下新建一个 index.js 文件(已有的话请忽略),index.js 文件编辑如下

    1. import Vue from 'vue';
    2. import Vuex from 'vuex';
    3. Vue.use(Vuex);
    4. let store = new Vuex.Store({
    5. state: {
    6. formData: {} // 企业提交数据表单对象
    7. }
    8. });
    9. export default store;
  • 在 src 目录下的 main.js 文件中引入 vuex 文件,并在实例化时添加配置

    1. import Vue from 'vue';
    2. import App from './App';
    3. import router from './router';
    4. import store from './store'; // 引入 vuex
    5. Vue.config.productionTip = false;
    6. Vue.http.options.emulateJSON = true;
    7. /* eslint-disable no-new */
    8. new Vue({
    9. el: '#app',
    10. router,
    11. store, // 需要在添加
    12. components: { App },
    13. template: '<App/>'
    14. });
  • 之后就可以直接在需要的组件中直接引用,引用具体示例如下

    vue-实战各种小技巧(长期更新) - 图20 vue-实战各种小技巧(长期更新) - 图21 控制台成功输出 vue-实战各种小技巧(长期更新) - 图22

    11. .eslintrc.js 文件 rules 增加设置

    我的笔记

    12. vue 表单操作

    我的笔记

    13. 解决使用 vux 组件库时 与 rem 设置冲突带来的问题

  • 思路

    将之前 rem 计算的数值 html font-size: "100px",改到 12px,之后连锁的将 less 中计算和引用的的值也改下,之后就可以了,尽量做到少量的修改即可

  • 将之前的 js 计算 rem 数值脚本修改相应的数值

    vue-实战各种小技巧(长期更新) - 图23 改过之后的 vue-实战各种小技巧(长期更新) - 图24

  • 修改 less

    vue-实战各种小技巧(长期更新) - 图25 改过之后的 vue-实战各种小技巧(长期更新) - 图26

  • 之后就可以是 rem 和 vux 基本正常了
    ##14. 通过 watch 动态的监测路由跳转(跳转时)和 APP.vue 中设置 created 方法实时监测 path (刷新时),来实现 header 文字的改变

  • header.vue
    1. watch: {
    2. '$route' (to, from) {
    3. // 检测路由改变 header 内容
    4. if (to.name === 'Index') {
    5. this.$store.state.PageTitle = '预约领号';
    6. this.$store.state.isShowBack = false;
    7. } else if (to.name === 'PreferentialDescription') {
    8. this.$store.state.PageTitle = '优惠说明';
    9. this.$store.state.isShowBack = true;
    10. } else if (to.name === 'RuleIntroduction') {
    11. this.$store.state.PageTitle = '规则简介';
    12. this.$store.state.isShowBack = true;
    13. } else if (to.name === 'ReservationSuccess') {
    14. this.$store.state.PageTitle = '预约排号';
    15. this.$store.state.isShowBack = true;
    16. }
    17. }
    18. }

    15. vue-router spa (单页)需要的 nginx 配置,防止出现 404 的情况

    官方文档 vue-实战各种小技巧(长期更新) - 图27 照着上方的图将代码复制至服务器的 nginx.config 配置文件即可

  1. ssh 远程登录服务器

    1. ssh username@ipaddress
    2. enter your password
  2. 查找服务器中的 nginx 相关目录,我这边是 nginx 服务器

    1. whereis nginx

    vue-实战各种小技巧(长期更新) - 图28

  • /etc/nginx 这个是 nginx 相关配置的目录
  • /usr/share/nginx 是静态文件的目录
  • 进入 html 目录,这个就是默认的存放项目文件的目录

    vue-实战各种小技巧(长期更新) - 图29

  • 修改 nginx 默认的配置文件

    1. # 首先进入配置文件目录
    2. cd /etc/nginx
    3. ls
    4. # 查看配置文件
    5. cat nginx.config
    6. # 复制一份原始的配置文件
    7. cp nginx.config nginx.config.back
    8. # 按照上面 vue-router 的需求修改配置文件
    9. vi nginx.config
    10. # 进入编辑状态
    11. I
    12. # 修改文件
    13. location / {
    14. try_files $uri $uri/ /index.html;
    15. }
    16. # 之后保存并退出
    17. esc
    18. :
    19. wq
    20. # 再次查看是否已修改成功
    21. cat nginx.config
    22. # 重载 nginx 配置文件(必须重载,不然修改的是不会生效的!)
    23. nginx -s reload

    vue-实战各种小技巧(长期更新) - 图30

  • 上面的步骤操作完成之后便解决了 vue-router spa 带来的刷新页面 404 的问题了,哦耶~

16. 与后台 API 进行通信时,Content-Type 请求文本格式未统一带来的问题

  • 问题截图

    vue-实战各种小技巧(长期更新) - 图31 后台返回 415 Unsupported Media Type 对于当前请求的方法和所请求的资源,请求中提交的实体并不是服务器中所支持的格式,因此请求被拒绝。

  • 产生错误的原因:

    这里使用的是 post 请求,后台的请求文本格式为 Content-Type:application/json;charset=UTF-8 但是这里使用的是默认的 Content-Type:application/x-www-form-urlencoded 所以造成的此次错误

  • 解决方法

    在提交数据之前使用 JSONstringify方法将数据转换为 json 格式的文本即可,如下图所示 vue-实战各种小技巧(长期更新) - 图32 JSON.stringiy() JSON.stringify() 方法是将一个JavaScript值(对象或者数组)转换为一个 JSON字符串,如果指定了replacer是一个函数,则可以替换值,或者如果指定了replacer是一个数组,可选的仅包括指定的属性。

    17. 清除 .vue 文件在 vscode 编辑器中格式化时默认添加的分号和双引号的规则

    参考地址 vue-实战各种小技巧(长期更新) - 图33

  • 问题

    因为 vue-cli 项目创建后会默认的增加 .eslintrc.js eslint 规则文件来帮助我们更好的统一代码的规范性,但是现在的趋势是省略 javascript 代码书写时在末尾添加的分号,但是 vscode 编辑器因为装了 vetur这个插件,所以还是会像之前的那样默认追加,使得项目报 eslint 语法的错误,单双引号也是相同的问题

  • 解决方法

    先安装扩展插件 Prettier - Code formatter vue-实战各种小技巧(长期更新) - 图34 之后在顶部菜单栏依次操作:【文件】->【首选项】->【设置】->【用户设置】 最后增加下面的规则代码片段

  1. "prettier.singleQuote": true,
  2. "prettier.semi": false

18. 之前的删掉了,等待更新中……
##19. 给 vue 挂载全局方法

  • 找到 main.js 文件进行编辑,这里以 axios 为例演示

    1. import Vue from 'vue'
    2. import axios from 'axios'
    3. Vue.prototype.axios = axios
  • 使用方法 某个 .vue 文件的 sccript 中如下编辑

    1. Vue.axios.post('url', { name: '' })
    2. .then(response => {
    3. console.log(response)
    4. })
    5. .catch(response => {
    6. console.log(response)
    7. })
    8. .finally(() => (me.loading= false));

    20. axios 不兼容 ie 的问题解决

  • 问题描述

    vue-实战各种小技巧(长期更新) - 图35 在 IE 浏览器下会报 “Promise”未定义” 的错误

  • 资料

    axios npmjs 地址 vue-实战各种小技巧(长期更新) - 图36 问题解决参考地址

  • 解决方法

    使用 babel-polyfill 这个包

  • 装包

    1. yarn add babel-polyfill --dev
  • 之后在 main.js 文件中引入包即可

    1. import 'babel-polyfill'

    vue-实战各种小技巧(长期更新) - 图37

  • 测试可兼容至 IE8+

    vue-实战各种小技巧(长期更新) - 图38

  • 更新 优化进阶 已经过测试

    参考文章里面有详细的解释:babel-polyfill使用与性能优化 目的是为了打包出更小的体积,下图是使用新方法打包出的体积,用上面的方法打包出来是 2.64MB vue-实战各种小技巧(长期更新) - 图39

  • 解决方法 将之前引入的 babel-polyfill 换成 core-js/es6/promise 这个是 vue-cli 脚手架就有的包无需再装了

    1. import 'core-js/es6/promise' // 解决 axios 兼容 IE 问题

    vue-实战各种小技巧(长期更新) - 图40

21. 组件封装,这里以 bootstrap 的 modal 模块为例

参考官方文档 — 组件基础 参考官方文档 — 组件注册 参考官方文档 — props

  • 先写组件,在 src -> components 目录下新建一个文件夹 msgmodal -> index.vue,编辑如下

    1. <template>
    2. <!-- 弹窗 -->
    3. <div class="modal fade" tabindex="-1" role="dialog" id="myModal">
    4. <div class="modal-dialog" role="document">
    5. <div class="modal-content">
    6. <div class="modal-header">
    7. <!-- <h5 class="modal-title"></h5> -->
    8. <button type="button" class="close" data-dismiss="modal" aria-label="Close">
    9. <span aria-hidden="true">&times;</span>
    10. </button>
    11. </div>
    12. <div class="modal-body">
    13. <p>{{ modalMsg }}</p>
    14. </div>
    15. <div class="modal-footer">
    16. <button type="button" class="btn btn-primary" data-dismiss="modal">确定</button>
    17. <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
    18. </div>
    19. </div>
    20. </div>
    21. </div>
    22. </template>
    23. <script>
    24. export default {
    25. name: 'MsgModal', // 定义的组件名称 使用时写法:msg-modal
    26. props: ['modalMsg'] // 定义的与父级通信的属性值 使用时写法:modal-msg
    27. }
    28. </script>
    29. <style scoped>
    30. </style>
  • 在具体的 .vue 文件使用组件,方法如下,这里是用的动态绑定的方法传递属性的值

    1. <!-- template -->
    2. <!-- 弹窗 -->
    3. <msg-modal :modal-msg="modalMsg"></msg-modal>
    4. // script
    5. import MsgModal from '@/components/msgmodal'
    6. export default{
    7. name:'App',
    8. components: { MsgModal },
    9. data () {
    10. return {
    11. // 弹窗信息 在执行操作时使用
    12. modalMsg: ''
    13. }
    14. }
    15. }
  • 测试,已实现可以实时更新内容了

    vue-实战各种小技巧(长期更新) - 图41

    22. 在 router -> index.js 按需引入模块,优化 SPA 页面的性能

    参考地址

  • 源码

    1. import Vue from 'vue'
    2. import Router from 'vue-router'
    3. import VueResource from 'vue-resource'
    4. // import index from '@/index'
    5. // import companyapply from '@/companyapply'
    6. // import choosenumber from '@/choosenumber'
    7. // import statelist from '@/statelist'
    8. Vue.use(Router)
    9. Vue.use(VueResource)
    10. export default new Router({
    11. // 去掉路由中自带的 #/ 这种东西
    12. mode: 'history',
    13. routes: [
    14. {
    15. path: '/',
    16. name: 'index',
    17. component: () => import('@/index')
    18. },
    19. {
    20. path: '/companyapply',
    21. name: 'companyapply',
    22. component: () => import('@/companyapply')
    23. },
    24. {
    25. path: '/choosenumber',
    26. name: 'choosenumber',
    27. component: () => import('@/choosenumber')
    28. },
    29. {
    30. path: '/statelist',
    31. name: 'statelist',
    32. component: () => import('@/statelist')
    33. }
    34. ]
    35. })

    23. 在 Vue 上挂载 vux 库中的 LoadingPlugin 组件

    官方文档地址 - 该组件支持以plugin形式调用

  • main.js

    1. // The Vue build version to load with the `import` command
    2. // (runtime-only or standalone) has been set in webpack.base.conf with an alias.
    3. import Vue from 'vue'
    4. import { LoadingPlugin } from 'vux' // 引入 looding 组件
    5. import App from './App'
    6. import store from './store' // 引入 vuex
    7. import router from './router'
    8. Vue.config.productionTip = false
    9. Vue.http.options.emulateJSON = true
    10. Vue.use(LoadingPlugin) // 挂载 loading 组件
    11. /* eslint-disable no-new */
    12. new Vue({
    13. el: '#app',
    14. router,
    15. store, // 需要在添加
    16. components: { App },
    17. template: '<App/>'
    18. })
  • .vue 文件中具体的使用示例:

    1. // loading
    2. this.$vux.loading.show({
    3. text: '数据提交中
    4. })
    5. // 隐藏 loading
    6. this.$vux.loading.hide()

    24. 解决:使用 axios 默认发送的是 application/json;charset=UTF-8 这种格式的数据后台无法读取的问题

    后台需要的是 application/x-www-form-urlencoded 这样的数据格式 参考地址

  • 问题截图

    vue-实战各种小技巧(长期更新) - 图42

  • 需要的格式截图

    vue-实战各种小技巧(长期更新) - 图43

  • 解决方法

    • 使用 qs 模块,转换数据格式

      vue-实战各种小技巧(长期更新) - 图44

    • 源码

      1. import qs from 'qs' // 解决 axios 数据提交格式与后台不一致的问题 -> name=hehe&age=10
      2. axios.post(postAPI,qs.stringify(postData))
      3. .then(request => {
      4. console.log(request)
      5. })
      6. .catch(error => {
      7. console.log(error)
      8. })

      25. 化繁为简的Watchers

      原文地址

  • 场景还原:

    1. created(){
    2. this.fetchPostList()
    3. },
    4. watch: {
    5. searchInputValue(){
    6. this.fetchPostList()
    7. }
    8. }

    组件创建的时候我们获取一次列表,同时监听input框,每当发生变化的时候重新获取一次筛选后的列表这个场景很常见,有没有办法优化一下呢?

  • 招式解析:

    首先,在watchers中,可以直接使用函数的字面量名称;其次,声明immediate:true表示创建组件时立马执行一次。

  1. watch: {
  2. searchInputValue:{
  3. handler: 'fetchPostList',
  4. immediate: true
  5. }
  6. }

26. 父子组件通信 — 子组件可以调用父组件的方法

  • 实现思路

    使用 this.$emit() 以下示例为分页组件,下面只是将主要的部分代码贴出

  • 父组件

    1. <template>
    2. <!-- 分页 -->
    3. <pagination @updatePageData="loadPageData"></pagination>
    4. </template>
    5. <script>
    6. export default{
    7. methods:{
    8. loadPageData:function(){
    9. // do something
    10. }
    11. }
    12. }
    13. <script>
  • 上面代码的说明

    @updatePageData="loadPageData" :传递方法时前面使用 @updatePageData 是给子组件使用的 父组件 loadPageData 方法的别名

  • 子组件

    1. export default{
    2. methods:{
    3. pageGo:function(){
    4. const me = this
    5. // 调用父组件方法
    6. me.$emit('updatePageData')
    7. }
    8. }
    9. }

    27. 父子组件通信 — 子组件可动态获取父组件的数据

  • 问题描述

    由于父组件的数据是动态获取的,而子组件初始化时如果获取不到数据就 Game Over

  • 实现思路

    使用 props 传数据 ; watch 监听数据 以下示例为分页组件,下面只是将主要的部分代码贴出

  • 父组件

    1. <template>
    2. <!-- 分页 -->
    3. <pagination :parentPageData="pageGetData"></pagination>
    4. </template>
    5. <script>
    6. export default{
    7. data(){
    8. return{
    9. pageGetData:[]
    10. }
    11. },
    12. methods:{
    13. getData:function(){
    14. // ajax 请求之后改变 pageGetData 的数据
    15. }
    16. }
    17. }
    18. <script>
  • 上面代码的说明

    :parentPageData="pageGetData" 传递方法前面使用 :parentPageData 是给子组件使用的 父组件 pageGetData 数据的别名

  • 子组件

    1. export default{
    2. props:['parentPageData'], // 父组件数据 别名
    3. watch:{
    4. // 监听父组件数据变化实时更新数据
    5. parentPageData:{
    6. handler: 'loadPageList',
    7. immediate: true
    8. }
    9. },
    10. methods:{
    11. // 加载页面数据
    12. loadPageList:function(){
    13. // do something
    14. }
    15. }
    16. }

    28. vue 多页面开发分页组件 有搜索功能

    文章笔记

29. Vue.vue 文件的样式 style 标签中使用 background:url() 引入图片

参考资料 vue 背景图引入

  • 示例代码片段
    1. <style scoped>
    2. .loading {
    3. position: fixed;
    4. left: 0;
    5. top: 0;
    6. background: url('~@/assets/img/loading-ball.svg') center center no-repeat #fff;
    7. width: 100vw;
    8. height: 100vh;
    9. z-index: 1000;
    10. }
    11. </style>

    30. 页面加载数据之前增加 loading 动画

    文章笔记

31. 封装一个 axios 的通用方法

  • 思路

    自定义一个函数,将其挂载到 Vue 对象的 prototype 上面,方便在页面使用,因为 axios 一般得和 qs 模块配合使用 qs 使用参考地址 vue-实战各种小技巧(长期更新) - 图45

  • main.js 文件中挂载自定义方法

    1. import Vue from 'vue'
    2. import 'babel-polyfill' // 解决 axios 兼容 IE 问题
    3. import qs from 'qs' // 解决 axios 数据提交格式与后台不一致的问题 -> name=hehe&age=10
    4. import axios from 'axios'
    5. // import router from './router'
    6. import '@/assets/css/common.css'
    7. import App from './index.vue'
    8. Vue.config.productionTip = false
    9. /**
    10. * 自定义一个方法封装 axios 请求,并将其挂载至 Vue 原型链
    11. * @param {string} url axios 请求的地址
    12. * @param {string} dataJson axios 向请求地址发送的 json 数据
    13. * @param {function} sucessFn axios 成功回调函数
    14. * @param {function} errorFn axios 失败回调函数
    15. */
    16. Vue.prototype.axiosFn = function(url, dataJson, sucessFn, errorFn) {
    17. axios
    18. .post(url, qs.stringify(dataJson))
    19. .then(response => {
    20. console.log(response)
    21. sucessFn()
    22. })
    23. .catch(error => {
    24. console.log(error)
    25. errorFn()
    26. })
    27. }
    28. /* eslint-disable no-new */
    29. new Vue({
    30. el: '#app',
    31. // router,
    32. components: { App },
    33. template: '<App/>'
    34. })
  • 具体的使用就不写了,只需要在调用方法 axiosFn 时给其传相应的参数即可

32. 满足:点击当前元素时,其子元素不会被点中,且还会执行回调事件—修改当前样式

  1. 思路:

    现在的需求是有一个列表,每个列表元素也是一个有子元素的嵌套元素,点击列表的每个子元素时给当前列表添加类名 正常使用官网提供的 事件处理 — Vue.js 文档,无法实现想要的效果,因为点击传递的 $event 是当前点击的事件,target 无法只得到列表的每个元素,相对其子元素而言就是父级元素 以前解决该方法的时候使用的是较为笨的方法:判断 target 元素,将其强行指向列表元素这样子

  2. 新方法

    给每个列表元素里面的子元素设置 css 属性 pointer-events: none;,这样的话也无需给 @click 添加什么修饰符即可实现需求

33. 子页面中在接口请求的时候,需要主页面 App.vue 中请求到的接口的返回值,特殊情况下会出现 Bug

  1. 错误重现

    vue-实战各种小技巧(长期更新) - 图46 顶部是公用的导航组件,数据在 App.vue 中请求,之后存进 store 中去,因为是 keep-alive ,所以正常情况下顶部的数据在第一个页面载入时便会储存到 store 中,但是如果是第二个页面(因为只有这个页面会需要上面的联系电话的数据)ctrl+f5 强制刷新后,store 中的数据会被清空,这样如果在子页面请求时没有及时的将主页面中请求到的数据联系电话存入 store 中的话,便会出现子页面请求时获取联系电话字段为空的情况,主要就是因为请求是同步发生的,加载的延迟很低,应该是主页面请求完成之后子页面再请求这样子。

  2. 解决 bug 后的源码示例 App.vue 文件

    1. <template lang="pug">
    2. #app
    3. .header-box(v-loading="layoutHeaderLoading")
    4. LayoutHeader
    5. .nav-box
    6. LayoutNav
    7. keep-alive
    8. .router-view(v-if="loadGuestRecord")
    9. router-view
    10. </template>
    11. <script>
    12. import LayoutNav from "@/components/LayoutNav";
    13. import LayoutHeader from "@/components/LayoutHeader";
    14. import { layoutHeader } from "@/api";
    15. export default {
    16. components: { LayoutNav, LayoutHeader },
    17. data() {
    18. return {
    19. // 是否加载 loading
    20. layoutHeaderLoading: false,
    21. // 顶部 header 数据对象
    22. headerInformation: {},
    23. // 解决 “联系电话”未储存到 store 中,所产生的 bug
    24. loadGuestRecord: true
    25. };
    26. },
    27. created() {
    28. const me = this;
    29. me.getPageData();
    30. },
    31. watch: {
    32. $route(to, from) {
    33. const me = this;
    34. // 对路由变化作出响应...
    35. to.path === "/guestRecord" &&
    36. !me.$store.getters.getMobile &&
    37. (me.loadGuestRecord = false);
    38. }
    39. },
    40. methods: {
    41. /**
    42. * 获取顶部 header 数据
    43. */
    44. getPageData() {
    45. const me = this;
    46. me.layoutHeaderLoading = true;
    47. layoutHeader
    48. .getCusUserInfo()
    49. .then(resolve => {
    50. console.log(resolve);
    51. resolve.data.code === 200 &&
    52. (me.headerInformation = resolve.data.data);
    53. me.$store.commit("updateMobile", me.headerInformation.mobile);
    54. })
    55. .catch(error => {
    56. console.log(error);
    57. })
    58. .finally(() => {
    59. me.layoutHeaderLoading = false;
    60. !me.loadGuestRecord && (me.loadGuestRecord = true);
    61. });
    62. }
    63. }
    64. };
    65. </script>
  3. 解决思路,解释上面的代码

    主要是顶部的全局请求.then( 单页请求 )这个样子的,下面是具体的实施方案: 在主页面 data 中增加一个布尔变量,默认为true,用它来指令是否 v-if 加载子页面 router-view ,watch.$route 中判断路由以及其他条件,为否 布尔变量 = false,再在主页面的请求中 .finally( 布尔变量 = true )

34. router-link 导航接收并传递全部的 url 参数

  1. 先说思路:

    在主页面 App.vue 文件中,接收所有的参数并将其传递给 nav 导航

  2. 源码示例:

  • 主文件 App.vue

    1. <template lang="pug">
    2. #app
    3. .header-box(v-loading="layoutHeaderLoading")
    4. LayoutHeader(:header-information="headerInformation")
    5. .nav-box
    6. LayoutNav(:url-query="urlQuery")
    7. keep-alive
    8. .router-view(v-if="loadGuestRecord")
    9. router-view
    10. </template>
    11. <script>
    12. import LayoutNav from "@/components/LayoutNav";
    13. import LayoutHeader from "@/components/LayoutHeader";
    14. import { layoutHeader } from "@/api";
    15. export default {
    16. components: { LayoutNav, LayoutHeader },
    17. data() {
    18. return {
    19. // 是否加载 loading
    20. layoutHeaderLoading: false,
    21. // 顶部 header 数据对象
    22. headerInformation: {},
    23. // 接受页面的所有参数,并将其赋值到 router-link 参数上面
    24. urlQuery: {}
    25. };
    26. },
    27. created() {
    28. const me = this;
    29. me.getPageData();
    30. },
    31. watch: {
    32. $route(to, from) {
    33. const me = this;
    34. me.urlQuery = to.query;
    35. }
    36. }
    37. };
    38. </script>
  • 组件 LayoutNav.vue

    1. <template lang="pug">
    2. nav.nav
    3. ul.nav-list
    4. li.nav-item(v-for="(item,index) in routers" :key="item.id")
    5. router-link(:to="{ name: item.link, query: urlQuery }")
    6. .nav-icon
    7. i(:class="item.icon")
    8. h3.nav-title {{ item.name }}
    9. </template>
    10. <script>
    11. import { routers } from "./config.json";
    12. export default {
    13. name: "layoutNav",
    14. props: {
    15. urlQuery: Object
    16. },
    17. data() {
    18. return {
    19. routers: routers
    20. };
    21. }
    22. };
    23. </script>

    35. 项目增加权限

  1. 思路:
  • 首先做的项目是从别的项目中跳转过来的,所以有权限的需求
  • 判断权限的主要步骤:
    • 先增加一个 401 无权限页面
    • 注册一个全局前置守卫
    • 在守卫中判断加密字符是否一致,路由跳转至相应的页面去
    • App.vue 文件中,使用全局的组件时,增加一个 布尔变量 控制是否展示组件,默认为 false 展示组件,true 是不展示组件,如果 watch.$route.to.path === "/401" ,这个 布尔变量 便为 true

36. 使用 beforeRouteLeave 销毁组件,实现组件的实时化

  • 代码

    1. // 导航离开该组件的对应路由时调用 [可以访问组件实例 `this`] https://router.vuejs.org/zh/guide/advanced/navigation-guards.html#%E7%BB%84%E4%BB%B6%E5%86%85%E7%9A%84%E5%AE%88%E5%8D%AB
    2. beforeRouteLeave(to, from, next) {
    3. // 销毁组件,避免通过 vue-router 再次进入时,仍是上次的 history 缓存的状态
    4. this.$destroy(true);
    5. next();
    6. }

    37. 使用官方提供的 component,根据条件循环判断使用不同的组件

  • 代码(其中 van 相关的组件使用方法请查看 vant 官网

    1. <template lang="pug">
    2. .integrated-query
    3. van-tabs(v-model="tabActive" color="#39a0ff" line-width="40" sticky)
    4. van-tab(v-for="item in tabTitleList" :key="item.value" :title="item.name" :name="item.value")
    5. component(:is="item.value")
    6. </template>
    7. <script>
    8. export default {
    9. name: "integratedQuery",
    10. components: {
    11. Overview: () => import("./components/Overview"),
    12. Product: () => import("./components/Product"),
    13. Accounting: () => import("./components/Accounting"),
    14. Order: () => import("./components/Order"),
    15. Assets: () => import("./components/Assets"),
    16. Discount: () => import("./components/Discount"),
    17. Invoice: () => import("./components/Invoice"),
    18. VirtualNetwork: () => import("./components/VirtualNetwork")
    19. },
    20. data() {
    21. return {
    22. tabTitleList: [
    23. {
    24. name: "总览",
    25. value: "overview"
    26. },
    27. {
    28. name: "产品",
    29. value: "product"
    30. },
    31. {
    32. name: "账务",
    33. value: "accounting"
    34. },
    35. {
    36. name: "订单",
    37. value: "order"
    38. },
    39. {
    40. name: "资产",
    41. value: "assets"
    42. },
    43. {
    44. name: "优惠",
    45. value: "discount"
    46. },
    47. {
    48. name: "发票",
    49. value: "invoice"
    50. },
    51. {
    52. name: "虚拟网",
    53. value: "virtual-network"
    54. }
    55. ],
    56. tabActive: "overview"
    57. };
    58. },
    59. }
    60. </script>

    38. 基于 vue-cli 脚手架 官方配置 在配置文件中 vue.config.js 引入样式预处理器共享的全局变量。

  • 配置方式

    1. module.exports = {
    2. /**
    3. * css 相关配置
    4. */
    5. css: {
    6. loaderOptions: {
    7. stylus: {
    8. import: "~@/common/stylus/mixin.styl"
    9. }
    10. }
    11. }
    12. }

    39. el-input 使用 @keyup.enter 无效

    需要添加 .native 修饰符

  • 代码示例

    1. el-input(placeholder="请输入内容" v-model="dataListQuery.queryKey" size="small" clearable @keyup.enter.native="getDataList(leftTreeSelect.id)")

    40. el-table 使用 toggleAllSelection 方法无效

    需要在函数中,增加一个参数 true

  • 代码示例

    1. this.$refs.dialogTable.toggleAllSelection(true)

    41. el-select 选中之后,数据无法回显

  • 需求描述

    目前是有一个弹窗,内部有一个表格,表格内部的每一条数据都是可以单独编辑的,有 input 也有 select vue-实战各种小技巧(长期更新) - 图47

  • 问题描述

    当我选中维表或者账期下拉选项之后,相对 v-model 绑定的数据字段已经成功更新了,但是在 el-select 组件中显示不出来字典相应的文本

  • 问题解决

    经过几个小时的反复试验,最终确定是由于我绑定的数据字段,并不是在数据初始化时(接口获取的时候)定义的,所导致的字段无法及时更新的问题。

  • 下面简单描述下我具体的错误方式:

    接口获取完数据,我并没有在这个时候增加一些需要初始化的字段,而是选择在将接口数据赋值给弹窗子组件的 form 之后,再在弹窗子组件中进行了字段的初始化,由于我每次赋值都是使用的 ES6 对象解构赋值的方式,所以最终导致解构出的变量已经不是最初始赋值时的对象,也就无法同步更新 form 表单中的数据。 所以最终的解决方法就是,在接口获取完数据,当时就增加一些初始化必要的字段。

42. 父组件使用 v-model 传值给子组件,实现子组件可以同步更新父组件的 v-model 绑定的值

  1. 参考资料

    官方提供的 model API csdn vue 父子组件使用v-model通信

  2. 具体实现的主要代码

  • 父组件

    1. <template lang="pug">
    2. .content
    3. child-el(v-model="data")
    4. </template>
    5. <script>
    6. import "childEl" from "./components"
    7. export default {
    8. components: {
    9. childEl
    10. },
    11. data() {
    12. return {
    13. data: ""
    14. }
    15. }
    16. }
    17. </script>
  • 子组件

    1. <template lang="pug">
    2. .content
    3. .text(@click="handleClick") {{ data }}
    4. </template>
    5. <script>
    6. export default {
    7. model: {
    8. prop: "data",
    9. event: 'change'
    10. },
    11. props: {
    12. data: {
    13. type: String,
    14. default: ""
    15. }
    16. },
    17. methods: {
    18. handleClick() {
    19. this.$emit("change", "测试文本")
    20. }
    21. }
    22. }
    23. </script>

    43 当使用 v-for 循环生成 dom 时,当需要再增加 ref 属性时,获取组件时需要加一个下标 [0]

  1. 代码
    1. //- 组件容器
    2. .tab-content(v-for='item in navListData', :key='item.value')
    3. .tab-bar {{ item.name }}
    4. component(:is='item.value', :ref='item.value')
    1. const componentsList = ['Overview', 'Volume', 'Industry', 'Monographic']
    2. componentsList.forEach((element) => {
    3. const component = this.$refs[`${element}`]
    4. console.log(component)
    5. component && component.length && component[0].getPageData(this.pagePostData)
    6. })