文章目录
前言
本篇文章最后通过八皇后的一小游戏来贯穿Vue的基础用法。
一、Hello Vue
1. Vue CIL
npm install -g @vue/cli
// 或者
yarn global add @vue/cli
1.1. 创建项目
vue create my-project
// 或者通过vue ui 可视化面板创建
vue ui
// 配置根据自己需求选择
1.2. 运行项目
cd my-project
npm run serve
//运行完毕后,复制链接到浏览器打开
//或者通过vue ui 可视化面板运行
2. 单文件组件
1. 提供了更好的封装性;
2. 优雅的模板支持;
3. scoped CSS支持;
4. 通过vue-loader可以配合各种预处理器进行构建;
3. 命名规范
在计算机科学中只有两件困难的事情:缓存失效和命名规范。——phil Karlton
// 命名规则
1. 小驼峰——camelCase: videoExampleComponent(js函数、变量);
2. 大驼峰——PascalCase: VideoExampleComponent(js类、组件文件);
3. 烤串——kebab-case: video-example-component(HTML中不区分大小,使用烤串式命名);
二、模板
1. 模板语法
Vue.js使用了基于HTML的模板语法,所有Vue.js的模板都是合法的HTML,所以能被遵循规范的浏览器和HTML解析器解析。
// XSS攻击
XSS攻击(Cross Site Scripting跨站脚本攻击)通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。
这些恶意网页程序通常是JavaScript,但实际上也可以包括Java、 VBScript、ActiveX、 Flash 或者甚至是普通的HTML。
攻击成功后,攻击者可能得到包括但不限于更高的权限(如执行一些操作)、私密网页内容、会话和cookie等各种内容。
// UGC
UGC 互联网术语,全称为User Generated Content,也就是用户生成内容,即用户原创内容。
UGC的概念最早起源于互联网领域,即用户将自己原创的内容通过互联网平台进行展示或者提供给其他用户。
UGC是伴随着以提倡个性化为主要特点的Web2.0概念而兴起的,也可叫做UCC(User-created Content)。
它并不是某一种具体的业务,而是一种用户使用互联网的新方式,即由原来的以下载为主变成下载和上传并重。
2. 指令
指令(Directives)是带有v-前缀的特殊特性
// v-bind 可简写成 :
// v-on 可简写成 @
// v-if 可简写成 :
// v-show 可简写成 @
// 不推荐在同一元素上使用 v-if 和 v-for
// 非要用,记住 v-for 的优先级高于 v-if
// ref——当需要在JS中直接访问一个子组件或者模板上的DOM节点,可以通过ref赋值一个ID引用。
// ref只有在模板被渲染之后才能被访问
3. JSX
// 渲染函数render()
<template>
<div class="hello">template</div>
</template>
// 使用JSX的方式实现上边的内容
<sprict>
exprot default {
props: {
msg: String
},
data() {
return {
title: "hello xiao ming"
};
},
render() {
return <div class="hello">template</div>;
}
};
</sprict>
// 条件渲染
<sprict>
exprot default {
props: {
msg: String
},
data() {
return {
user: {
age: 19,
name: "Xiao Ming"
}
};
},
render() {
if(this.user.age > 18) {
return <div>Welcome, {this.user.name}</div>
}
return <div>No Log</div>;
}
};
</sprict>
三、数据
1. Prop & Data
// Data
<sprict>
exprot default {
props: {
msg: String
},
data() {
return {
user: {
age: 19,
name: "Xiao Ming"
}
};
},
render() {
if(this.user.age > 18) {
return <div>Welcome, {this.user.name}</div>
}
return <div>No Log</div>;
}
};
</sprict>
// data是一个函数的原因是什么?
// 因为只有返回一个生产data的函数,这个组件产生的每一个实例才能维持一份被返回对象的独立的拷贝。
// Prop 用于父组件向子组件传参(vue是单向数据流——只能从父组件到子组件,返回报错(主要是子组件修改父组件数据,响应式的,不知道嵌套了多少层父级,应用数据难以理解))
// 父组件
<template>
<div>
<subset parent-num="2"></subset >
</div>
</template>
<sprict>
import Subset from "./Subset";
exprot default {
components: {
Subset
}
};
</sprict>
// 子组件
<template>
<div>{{ parentNum }}</div>
</template>
<sprict>
exprot default {
props: ["parentName"]
};
</sprict>
2. 计算属性和侦听器
// 计算属性computed(在组件的模板中,使用一些常量内容,将其放到data中会做响应式收集,这时我们可以使用computed的缓存特性,进行优化)
1. 计算属性是基于其内部的响应依赖进行缓存的;
2. 只在相关响应式依赖发生改变时它们才会重新求值;
// 方法
1. 无缓存;
2. 每当触发重新渲染时,调用方法将总会再次执行函数
// 侦听器watch(和computed一样具有监听的能力,但computed在数据变化后执行异步操作或者开销较大的操作时,会消耗内存,阻塞渲染,此时在watch中操作)
3. 数组操作
// 在vue2.0中,是通过Object.defineProperty对data中的属性进行遍历,并将其转化成为getsetter,开发中getsetter函数是不可见的,在内部它是为了追踪依赖,在访问和修改的时候通知变更,这导致了vue在数据操作中的一些局限性。
1. 不能检测对象属性的添加或者删除;
2. 不能检测到数组长度变化(通过改变length而增加的长度不能监测到);
3. 不是因为defineProperty的局限性,而是出于性能考量的,不会对数组每个元素都监听;
// 解决方法
// 添加
1. 全局方法 Vue.set();
2. Vue实例方法 this.$set();
// 删除
1. 全局方法 Vue.delete();
2. Vue实例方法 this.$delete();
// vue 中添加响应式依赖的api
1. push();
2. pop();
3. shift();
4. unshift();
5. splice();
6. sort();
7. reverse();
四、事件 & 样式
1. 事件
<template>
<div>
{{ count }}
<button v-on:click="add">add</button>
</div>
</template>
<sprict>
import Subset from "./Subset";
exprot default {
data() {
return {
count: 9
};
},
methods: {
add(event) {
// this 在方法里指向当前 Vue 实例
this.count += 1;
// event 是原生 DOM 事件
if(event) {
alert(event instanceof MouseEvent);
}
}
}
};
</sprict>
// 即能获取event(原生 DOM 事件),又能传参
<template>
<div>
{{ count }}
<button v-on:click="add($event, 2)">add</button>
</div>
</template>
<sprict>
import Subset from "./Subset";
exprot default {
data() {
return {
count: 9
};
},
methods: {
add(event,num) {
// this 在方法里指向当前 Vue 实例
this.count += 1;
// event 是原生 DOM 事件
if(event) {
alert(event instanceof MouseEvent);
}
}
}
};
</sprict>
2. 事件修饰符
事件修饰符是为了保证methods方法只有纯粹的数据逻辑(和DOM解耦,易于单元测试),不去处理DOM相关的操作。
<template>
<div>
{{ count }}
<!-- .stop阻止默认冒泡 -->
<button @click.stop="add">add</button>
</div>
</template>
<sprict>
import Subset from "./Subset";
exprot default {
data() {
return {
count: 9
};
},
methods: {
add(event) {
// this 在方法里指向当前 Vue 实例
this.count += 1;
// event 是原生 DOM 事件
if(event) {
alert(event instanceof MouseEvent);
}
}
}
};
</sprict>
常用的时间修饰符
<template>
<div>
<!-- 阻止单击事件继续传播 -->
<a v-on:click.stop="doThis"></a>
<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThis"></a>
<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>
<!-- 添加事件监视器时使用事件捕获模式 -->
<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div v-on:click.capture="doThis">...</div>
<!-- 只当在event.target是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThis">...</div>
<!-- 点击事件只会触发一次 -->
<a v-on:click.once="doThis"></a>
<!-- 滚动事件的默认行为(即滚动行为)将会立即触发 -->
<!-- 而不会等待 onScroll 完成 -->
<!-- 这其中包含 event.preventDefault() 的情况 -->
<div v-on.scroll.passive="onScroll">...</div>
<!-- 只有在 key 是 Enter 时调用 vm.submit() -->
<input v-on:keyup.enter = "submit" />
<input v-on:keyup.page-down = "onPageDown" />
<input v-on:keyup.13 = "submit" />
<!-- Alt + C -->
<input v-on:alt.67 = "clear" />
<!-- Ctrl + Click -->
<div @click.ctrl="doSomething">Do something</div>
<!-- 即使 Alt 或 Shift 被一同按下时也会触发 -->
<button @click.ctrl="onClick">A</button>
<!-- 有且只有Ctrl被按下的时候才触发 -->
<button @click.ctrl.exact="onCtrlClick">A</button>
<!-- 没有任何系统修饰符被按下的时候才触发 -->
<button @click.exact="onCtrlClick">A</button>
</div>
</template>
3. 自定义事件(子组件向父组件传递信息)
// 父组件
<template>
<div>
{{ num }}
<subset @update:num="num = $event"></subset >
</div>
</template>
<sprict>
import Subset from "./Subset";
exprot default {
components: {
Subset
},
data() {
return {
num: 9
};
},
};
</sprict>
// 子组件
<template>
<div @click="$emit('update:num', 100)">数字num=100</div>
</template>
<sprict>
exprot default {};
</sprict>
4. 双向绑定
// 父组件
<template>
<div>
{{ num }}
<subset :num="num" @update:num="num = $event"></subset>
<!-- 使用修饰符简写(:num="num" @update:num="num = $event"简写为:num.sync="num",要求事件必须为@update:的形式) -->
<!-- <subset :num.sync="num"></subset> -->
</div>
</template>
<sprict>
import Subset from "./Subset";
exprot default {
components: {
Subset
},
data() {
return {
num: 9
};
},
};
</sprict>
// 子组件
<template>
<!-- 自定义事件向父组件传递数据 -->
<div @click="$emit('update:num', 100)">数字{{ num }}</div>
</template>
<sprict>
exprot default {
props: ["num"] //子组件接收父组件传过来的数据
};
</sprict>
5. 样式
“在vue中,vm是ViewModel的缩写,是视图模型的意思,是连接view和model的桥梁;ViewModel能够监听到数据的变化,然后通知到对应的视图做自动更新,实现双向绑定。”
// vm动态控制class类名
<template>
<div v-bind:class="{'ui-demo-success': state,'ui-demo-err': hasError,}">数字</div>
</template>
<sprict>
exprot default {
data() {
return {
state: true, //为true时绑定类名ui-demo-success
hasError: false //为false时不绑定类名ui-demo-err
};
},
};
// 在false/0/""/null/undefined/NaN中,都可以表示false,其他的都表示true
五、组件 & 生命周期
1. 组件注册
1. 全局注册
全局注册的组件可以在任何地方使用;
Vue.component("custom", {
render() {
return <div>custom</div>;
}
});
// 全局注册的组件在最终打包的时候都会被打包进去
2. 局部注册
局部注册的组件只能在当前组件中使用;
<template>
<div>
<subset></subset>
</div>
</template>
<sprict>
import Subset from "./Subset";
exprot default {
components: {
Subset
},
};
</sprict>
3. 按需载入
// babel-plugin-import
// babel-plugin-component (element在babel-plugin-import基础上再封装的)
不同组件库的开发文档中都会介绍其使用方式;
2. 生命周期
钩子函数 | 简单介绍 |
---|---|
beforeCreate | 最初调用触发,data和events都不能用。可以在这里处理整个系统加载的Loading。 |
created | 已经具有响应式的data,可以发送events。可以在这里去发送请求 |
beforeMount | 在模板编译后,渲染之前触发。SSR中不可用。基本用不上这个Hook。 |
mounted | 在渲染之后触发,并可访问组件中的DOM以及$ref,SSR中不可用 |
beforeUpdate | 在数据改变后、模板改变前触发。切勿使用它监听数据变化(使用计算属性和watch监听)。 |
updated | 在数据改变后、模板改变后触发。常用于重渲染后的打点、性能检测或者触发vue组件中非vue组件的更新。 |
beforeDestroy | 组件卸载前触发,可以在此时清理事件、计时器或者取消订阅操作。 |
destroyed | 卸载完毕后触发,可以做最后的打点或事件触发操作。 |
3. 动态组件
3.1 component组件标签
// component组件标签方式通过v-bind:is来确定展示哪个组件,组件可以不用在components中声明
<template>
<component :is="currentComponent"></component>
</template>
<sprict>
import Subset1 from "./Subset1";
import Subset2 from "./Subset2";
exprot default {
data() {
return {
state: true,
};
},
computed: {
currentComponent() {
return this.state ? Subset1 : Subset2;
}
}
};
</sprict>
3.1 keep-alive组件标签
// keep-alive标签可以缓存component组件标签中的实例(来回切换展示的组件时,可以缓存下来)
<template>
<keep-alive>
<component :is="currentComponent"></component>
</keep-alive>
</template>
<sprict>
import Subset1 from "./Subset1";
import Subset2 from "./Subset2";
exprot default {
data() {
return {
state: true,
};
},
computed: {
currentComponent() {
return this.state ? Subset1 : Subset2;
}
}
};
</sprict>
总结
革命尚未成功,同志仍需努力。