Vue+ 项目学习总结

1600178283331.jpg

近日独立完成一个小项目搭建,总结​个人踩坑和基础内容知识。内容比较基础,也希望能有高手看到后可以指点一二。项目中引入的都是必须和用到的内容,研究后选用​:Vue, vue-router, vuex, vant, axios


Vue 之 computed & watch & method用法区分

Vue 中我们都可以通过这method实现方法和参数改变,但是细读Vue组件,我认为这几个方法的使用被我们开发过程中忽视了。

computed 计算属性

  • 有缓存,可以提高性能, 在HTML DOM加载后马上执行的,如赋值
  • 你可以像绑定普通 property 一样在模板中绑定计算属性。创建依赖关系:计算属性的 getter 函数是没有副作用 (side effect) 的,这使它更易于测试和理解。
  • VS methods :,在methods可以达到和computed一样的效果。然而,不同的是计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要 message 还没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数。计算属性有缓存,只要依赖的数据不变,就可以避免多次请求getter函数。

watch

异步返回参数的可以用watch,开销比较大
— 观察 Vue 实例上的一个表达式或者一个函数计算结果的变化。回调函数得到的参数为新值和旧值。表达式只接受简单的键路径。
— options: immediate:
在选项参数中指定 immediate: true 将立即以表达式的当前值触发回调
— options: deep 为了发现对象内部值的变化,可以在选项参数中指定

项目中,当遇到观察对象是数组,并且每项是对象的情况下,需要设置这两个选择

methods

需要一定的出发条件才能执行的


Vue 中内置对象的数据更新

Vue data 是一个方法,返回的是一个对象。实例化后每个实例的data是不同的对象。

  • 由于 JavaScript 的限制,Vue 不能检测数组和对象的变化,所以提供了set方法。向响应式对象中添加一个 property,并确保这个新 property 同样是响应式的,且触发视图更新。 Vue.set( target, propertyName/index, value )
    this.$set(this.goodList[this.index], ‘customPrice’, this.price)
    当商品的价格更新了后,同时也更新视图。
  • Vue.delete( target, propertyName/index ) 删除对象的 property。如果对象是响应式的,确保删除能触发更新视图。这个方法主要用于避开 Vue 不能检测到 property 被删除的限制
    this.$delete( this.goodList[this.activeKey], col )

在组件内使用 vm.$nextTick(). Vue 在更新 DOM 时是异步执行的. 只要侦听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。如果同一个 watcher 被多次触发,只会被推入到队列中一次。

所以当someData更新后,不会立马渲染,如果想插队的话,可以使用Vue.nextTick(callback)方法。
因为 $nextTick() 返回一个 Promise 对象,所以你可以使用新的 ES2017 async/await 语法完成相同的事情:

methods: {
  updateMessage: async function () {
    this.message = '已更新'
    console.log(this.$el.textContent) // => '未更新'
    await this.$nextTick()
    console.log(this.$el.textContent) // => '已更新'
  }
}

VUE 父子组件的通信问题

父组件给子组件传参props

所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。

每次父级组件发生变更时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。故要么在data重新定义参数,用props值进行初始化,或者用computed进行转换

Props验证中遇到问题:
Props with type Object/Array must use a factory function to return the default value.

 // 报错代码
    props: {
        cartList: {
            type: Array,
            default:[]
        }
    },

/// 正解1   箭头函数/
    props: {
        cartList: {
            type: Array,
            default:()=>[]
        }
    }
    /// 正解2/
    props: {
        cartList: {
            type: Array,
            default:function(){
                return [];
            }
        }
    }
父组件使用子组件方法

可以给子组件一个”ref = “item””的名字,那么在父组件中可以this.$refs.item.closePopup() 通过refs去调用子组件方法。
Ref 被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs 对象上。
关于 ref 注册时间的重要说明:因为 ref 本身是作为渲染结果被创建的,在初始渲染的时候你不能访问它们 - 它们还不存在!$refs 也不是响应式的,因此你不应该试图用它在模板中做数据绑定。

如何对一个props进行“双向绑定”

无法实现真正的双向绑定,vue是单向数据流的,所以可以使用update:myPropName 的模式触发事件取而代之。

// 父组件可以监听那个事件并根据需要更新一个本地的数据 property


// 子组件中
this.$emit('update:title', newTitle)

父组件也可以用sync来简写:


Vue中的事件修饰符

  • .stop: 阻止冒泡(通俗讲就是阻止事件向上级DOM元素传递)
    点击内层的div后,不会向外层的div传递
  • . prevent:阻止默认事件的发生
    默认事件指对DOM的操作会引起自动执行的动作,比如点击超链接的时候会进行页面的跳转,点击表单提交按钮时会重新加载页面等,使用”.prevent”修饰符可以阻止这些事件的发生。
  • . capture:捕获冒泡,即有冒泡发生时,有该修饰符的dom元素会先执行,如果有多个,从外到内依次执行,然后再按自然顺序执行触发的事件。
  • . self:将事件绑定到自身,只有自身才能触发,通常用于避免冒泡事件的影响
  • . once:设置事件只能触发一次,比如按钮的点击等。
  • . native:在父组件中给子组件绑定一个原生的事件,就将子组件变成了普通的HTML标签,不加’. native’事件是无法触 发的。

Vue 中.env 环境配置搭建

达到的效果
在不同环境中配置环境参数,需要使用的时候,直接用环境变量就可以,可以不用hard code。
在运行打包环境的时候,可以直接用不同的命令行就可区分环境打包。
如下所示:

"scripts": {
    "serve": "vue-cli-service serve --open",
    "build": "vue-cli-service build",
    "beta": "vue-cli-service build --mode beta",
    "lint": "vue-cli-service lint"
  },

Beta 环境 下就可以直接yarn beta

.env 文件需要手动自己新建,

VUE_APP_URL = '/'   // 用来设置项目的 baseUrl 和路由的 base 选项 默认是根目录
VUE_APP_MODE = ''   // 设置一个变量 区分线上生产环境和线上测试环境 默认是生产环境
outputDir = 'dist'  // 用来设置打包后生成的文件夹的名字,默认为 dist 文件夹

.env.beta

NODE_ENV = 'production'
VUE_APP_URL = 'https://aaa.bbb.cn'
VUE_APP_TITLE = A(beta)

.env.production

NODE_ENV = 'production'
VUE_APP_TITLE = A
VUE_APP_URL = 'https://ccc.bbb.cn'

踩坑的地方是,NODE_ENV的配置。我开始认为在beta环境下 NODE_ENV = ‘beta’ 用来区分不同环境的配置。这样配置的话,在运行yarn beta后,结果没有打包出js,和css 这样就需要自己去写vue.config.js的配置文件。
可是我也没有特殊需求,所以最后把NODE_ENV 写成生成环境即可。


Vue Router 的使用笔记

导航守卫

  • 全局守卫 [beforeEach] / [afterEach]
    当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于 等待中
const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
  // ...
})
  • 路由独享的守卫
    你可以在路由配置上直接定义 beforeEnter 守卫:
const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter: (to, from, next) => {
        // ...
      }
    }
  ]
})
  • 组件内的守卫
const Foo = {
  template: `...`,
  beforeRouteEnter (to, from, next) {
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不!能!获取组件实例 `this`
    // 因为当守卫执行前,组件实例还没被创建
  },
  beforeRouteUpdate (to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
  },
  beforeRouteLeave (to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
  }
}

完整的导航解析流程

  1. 导航被触发。
  2. 在失活的组件里调用 beforeRouteLeave 守卫。
  3. 调用全局的 beforeEach 守卫。
  4. 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
  5. 在路由配置里调用 beforeEnter。
  6. 解析异步路由组件。
  7. 在被激活的组件里调用 beforeRouteEnter。
  8. 调用全局的 beforeResolve 守卫 (2.5+)。
  9. 导航被确认。
  10. 调用全局的 afterEach 钩子。
  11. 触发 DOM 更新。
  12. 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。

项目中运用到 beforeRouteEnter & beforeEnter
Router 配置信息内的路由守卫beforeEnter先调用,后在调用组件内的beforeRouteEnter。在执行的顺序上是不一样的。


CSS & vant

  • 在修改vant 组件样式的时候,需要/deep/ 才能让深层的样式起作用。至于原因还没有查清楚

Array 常用方法总结

  • Array 赋值: 如何创建一个8位初始值是0的数组
    Let b = Array(8).fill(0) => (8) [0, 0, 0, 0, 0, 0, 0, 0]

  • String -> Array split() 把string文本按照格式分隔成数组
    var a = 'hello'; a.split('') => (5) ["h", "e", "l", "l", "o"]

  • Array splice 添加/删除/替换 数组项
    let arr.splice(2,3,”William”)

  • Array 数组扁平化(Array.prototype.flat() - JavaScript | MDN)
    var newArray = arr.flat([depth])

使用 reduce 与 concat

var arr = [1, 2, [3, 4]];

// 展开一层数组
arr.flat();
// 等效于
arr.reduce((acc, val) => acc.concat(val), []);
// [1, 2, 3, 4]

// 使用扩展运算符 ...
const flattened = arr => [].concat(...arr);
reduce + concat + isArray + recursivity

reduce + concat + isArray + recursivity

// 使用 reduce、concat 和递归展开无限多层嵌套的数组
var arr1 = [1,2,3,[1,2,3,4, [2,3,4]]];

function flatDeep(arr, d = 1) {
   return d > 0 ? arr.reduce((acc, val) => acc.concat(Array.isArray(val) ? flatDeep(val, d - 1) : val), [])
                : arr.slice();
};

flatDeep(arr1, Infinity);
// [1, 2, 3, 1, 2, 3, 4, 2, 3, 4]

  • Array : map, forEach,的区别
    — 返回值: map()方法会得到一个新的数组并返回 / forEach()会修改原来的数组。
arr.forEach((value, key) => {
 return arr[key] = value * value;
});
-> (8) [1, 4, 0, 9, 4, 4, 4, 4]

let list = arr.map(value => {
 return value * value;
});

-> 修改的是list,
list
(8) [1, 16, 0, 81, 16, 16, 16, 16]
arr
(8) [1, 4, 0, 9, 4, 4, 4, 4]

— 速度: forEach()的执行速度 < map()的执行速度

forEach适合于你并不打算改变数据的时候,而只是想用数据做一些事情 – 比如存入数据库或则打印出来。

map()适用于你要改变数据值的时候。不仅仅在于它更快,而且返回一个新的数组。这样的优点在于你可以使用复合(composition)(map(), filter(), reduce()等组合使用)来玩出更多的花样。

  • Array.reduce (https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce)
var sum = [0, 1, 2, 3].reduce(function (accumulator, currentValue) {
  return accumulator + currentValue;
}, 0);
// 和为 6

0 是accumulator的初始值
//累加对象数组里的值
var initialValue = 0;
var sum = [{x: 1}, {x:2}, {x:3}].reduce(function (accumulator, currentValue) {
    return accumulator + currentValue.x;
},initialValue)

console.log(sum) // logs 6

//计算数组中每个元素出现的次数
var names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];

var countedNames = names.reduce(function (allNames, name) { 
  if (name in allNames) {
    allNames[name]++;
  }
  else {
    allNames[name] = 1;
  }
  return allNames;
}, {});
// countedNames is:
// { 'Alice': 2, 'Bob': 1, 'Tiff': 1, 'Bruce': 1 }


以上是此次项目中总结到的关键性的内容。样式css这块还没有总结的很多,后面会慢慢积累。

参考文献:

  • https://cn.vuejs.org/
  • https://vuex.vuejs.org/zh/guide/
  • https://router.vuejs.org/zh/installation.html
  • https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array
  • https://blog.csdn.net/wuj1935/article/details/106554826?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param

你可能感兴趣的:(Vue+ 项目学习总结)