文章目录
语法
1.表单输入绑定
表单输入需要进行双向数据绑定,所以使用v-model
。
- 文本:
<input type="text" v-model="user" />
收集value值;type=password、text、number。 - 多行文本:
<textarea v-model="text"></textarea>
不接受插值语法; - 单选框:
<input type="radio" name="sex" v-model="userInfo.sex" value="male">
单选框需要标识name,否则不会排他,手机value值; - 多选框:
<input type="checkbox" v-model="userInfo.hobby" value="study">
:没有配置value属性,那么收集的是checked属性(勾选 or 未勾选,是布尔值);若配置了value属性:
①v-model的初始值是非数组,那么收集的就是checked(勾选 or 未勾选,是布尔值)
②v-model的初始值是数组,那么收集的就是value组成的数组 - 下拉框:
<select v-model="userInfo.city"> <option value="">请选择校区</option> <option value="beijing">北京</option> <option value="shanghai">上海</option> <option value="shenzhen">深圳</option> <option value="wuhan">成都</option> </select>
文本类型的 和 元素会绑定 value property 并侦听 input 事件;
和 会绑定 checked property 并侦听 change 事件;
会绑定 value property 并侦听 change 事件
v-model 会忽略任何表单元素上初始的 value、checked 或 selected attribute,应该在 JavaScript 中使用data 选项来声明该初始值。
v-model的三个修饰符:
- lazy:失去焦点后再收集数据,默认在input事件后收集数据,加lazy后在change事件后收集数据。
- number:输入字符串转为有效的数字
- trim:默认自动去除用户输入内容中两端的空格
过滤器:
定义:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)
注册过滤器:
Vue.filter(name, callback)全局过滤器
new Vue {filters: {}} 局部过滤器
使用过滤器:{
{ xxx | 过滤器名}}
或 v-bind:属性 = "xxx | 过滤器名"
- 过滤器可以接收额外参数,多个过滤器也可以串联
- 并没有改变原本的数据,而是产生新的对应的数据
2. 一些内置指令
v-text
:v-text会替换掉节点中的内容,<div v-text="str"></div>
;v-html
:向指定节点中渲染包含html结构的内容 ,会替换掉节点中所有的内容,<div v-html="str"></div>
;v-cloak
:本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性;所以可以在加载vue慢的时候使用v-cloak属性接管节点,设置该属性不可见,解决{ {xx}}问题;v-once
:所在节点在初次动态渲染后,就视为静态内容了,以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能;v-pre
:跳过v-pre所在节点的编译过程,可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译
自定义指令:
- 局部指令:
new Vue({
directives:{
指令名:配置对象
}
})
new Vue({
directives:{
指令名:回调函数
}
})
- 全局指令
Vue.directive(指令名, 配置对象)
或
Vue.directive(指令名, 回调函数)
Vue.directive('fbind', {
// 指令与元素成功绑定时(一上来)
bind(element, binding) {
// element就是DOM元素,binding就是要绑定的
element.value = binding.value
},
// 指令所在元素被插入页面时
inserted(element, binding) {
element.focus()
},
// 指令所在的模板被重新解析时
update(element, binding) {
element.value = binding.value
}
})
3.Vue生命周期
- 生命周期又名生命周期回调函数、生命周期函数、生命周期钩子
- 是Vue在关键时刻帮我们调用的一些特殊名称的函数
- 生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的
- 生命周期函数中的 this 指向是vm或组件实例对象
Vue 完成模板的解析并把初始的真实 DOM 元素放入页面后(挂载完毕)调用 mounted;
数据代理与数据监测:创建前、创建完毕;
虚拟DOM:将要挂载与挂载完毕;
model->view:将要更新与更新完毕;
所有:将要销毁和销毁完毕。
4.组件基础
组件是用来实现局部功能的代码和资源的集合。
组件允许我们将 UI 划分为独立的、可重用的部分,并且可以对每个部分进行单独的思考。在实际应用中,组件常常被组织成层层嵌套的树状结构:
优点:复用编码,简化项目编码,提高运行效率。
非单文件组件:一个文件中包含有 n 个组件
单文件组件:一个文件中只包含有 1 个组件,后缀.vue
使用非单文件组件:(不推荐)
创建:
const 组件名 = Vue.extend({
options})
- 配置项中el不要写,因为最终所有的组件都要经过一个vm的管理,由vm中的el才决定服务哪个容器
- data必须写成函数,避免组件被复用时,数据存在引用关系
注册:
//局部注册
new Vue({
el:'',
data:{
},
components:{
组件名:组件}
})
//全局注册
Vue.component('组件名',组件)
脚手架里才能处理自闭合标签。如果你是直接在 DOM 中书写模板 (例如原生 元素的内容),模板的编译需要遵从浏览器中 HTML 的解析行为,必须显式使用闭合标签。
VueComponent:
- school 组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,而是 Vue.extend() 生成的
- 我们只需要写 或 ,Vue 解析时会帮我们创建 school 组件的实例对象,即Vue帮我们执行的new VueComponent(options)
- 每次调用Vue.extend,返回的都是一个全新的VueComponent,即不同组件是不同的对象
- 组件配置中data函数、methods中的函数、watch中的函数、computed中的函数 它们的 this 均是 VueComponent实例对象
- new Vue(options)配置中:data函数、methods中的函数、watch中的函数、computed中的函数 它们的 this 均是 Vue实例对象
- VueComponent的实例对象,以后简称vc(组件实例对象)Vue的实例对象,以后简称vm
- VueComponent.prototype.proto === Vue.prototype,让组件实例对象vc可以访问到 Vue原型上的属性、方法
单文件组件:
- 命名采用驼峰命名法;
- template中必须有且只有一个根元素;
- 创建一个vue文件:template标签(结构),script标签(交互),style标签(样式),注意要export default暴露,要写name。
<template>
<button @click="count++">You clicked me {
{
count }} times.</button>
</template>
<script>
export default {
name:'组件名',
data() {
return {
count: 0
}
}
}
</script>
- 使用:引入组件,注册,然后使用标签;
<script>
import ButtonCounter from './ButtonCounter.vue'
export default {
components: {
ButtonCounter
}
}
</script>
<template>
<h1>Here is a child component!</h1>
<ButtonCounter />使用
</template>
介绍一下vue项目总体结构:
- 准备好自己设计的单文件组件vue文件;
- 创建
APP.vue
文件,调用组件; - 然后创建main.js,调用
App.vue
; - 创建index.html,引入main.js。
如果main.js中不想写任何东西,可以按照如下编写:
new Vue({
template:`<App></App>`,
el:'#root',
components:{
App}
})
5.Vue CLI脚手架
render函数(渲染函数):用js语言来构建DOM,因为vue是虚拟DOM,所以在拿到template模板时也要转译成VNode的函数,而用render函数构建DOM,vue就免去了转译的过程。当引入残缺的vue时,用render。render 函数 跟 template 一样都是创建 html 模板的,但是有些场景中用 template 实现起来代码冗长繁琐而且有大量重复,这时候就可以用 render 函数。
new Vue({
el:'#app',
// render函数功能:将App组件放入容器中
// 简写形式
render: h => h(App),
// 完整形式
// render(createElement){
// return createElement(App)
// }
})
6.组件深入
ref属性:
- ref被用来给元素或子组件注册引用信息(id的替代者)应用在html标签上获取的是真实DOM元素,应用在组件标签上获取的是组件实例对象vc
- 使用方式
a:打标识:<h1 ref="xxx"></h1>
或<School ref="xxx"></School>
b:获取:this.$refs.xxx
props配置项:
- 让组件接收外部传过来的数据;
- props优先级高于data
- 传递数据
<Demo name="xxx" :age="18"/>
这里age前加:
,通过v-bind使得里面的18是数字 - 接收数据
第一种方式(只接收)props:[‘name’, ‘age’]
第二种方式(限制类型)props:{name:String, age:Number}
第三种方式(限制类型、限制必要性、指定默认值)
props: {
name: {
type: String, // 类型
required: true,// 必要性
default: 'cess'// 默认值
}
}
注:props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制props的内容到data中,然后去修改data中的数据。
mixin:
功能:可以把多个组件共用的配置提取成一个混入对象。
数据和方法冲突时,组件优先;生命周期钩子全要。混入对象的钩子将在组件自身钩子之前调用。
定义:
const mixin = {
data() {
....},
methods: {
....}
....
}
使用:
- 全局混入
Vue.mixin(xxx)
- 局部混入
mixins:['xxx']
7.插件
功能:用于增强Vue
本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据。
可以在其中写些全局操作。
定义插件:
export default {
install(Vue,x,y,z){
}
}
使用插件:Vue.use(插件名,数据)
8.scoped样式
作用:让样式在局部生效,防止冲突
写法:<style scoped>
9.浏览器的本地存储
WebStorage(js 本地存储)
存储内容大小一般支持 5MB 左右(不同浏览器可能还不一样)
浏览器端通过Window.sessionStorage和Window.localStorage属性来实现本地存储机制
相关API
xxxStorage.setItem(‘key’, ‘value’)该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应的值
xxxStorage.getItem(‘key’)该方法接受一个键名作为参数,返回键名对应的值
xxxStorage.removeItem(‘key’)该方法接受一个键名作为参数,并把该键名从存储中删除
xxxStorage.clear()该方法会清空存储中的所有数据
备注
- SessionStorage存储的内容会随着浏览器窗口关闭而消失
- LocalStorage存储的内容,需要手动清除才会消失
- xxxStorage.getItem(xxx)如果 xxx 对应的 value 获取不到,那么getItem()的返回值是null
- JSON.parse(null)的结果依然是null
10.组件间交互
子组件想给父组件传数据,那么就要在父组件中给子组件绑定自定义事件(事件的回调在A中)。
- 绑定自定义事件
- 第一种方式,在父组件中
<Demo @事件名="方法"/>
或<Demo v-on:事件名="方法"/>
- 第二种方式,在父组件中的mouted中
this.$refs.demo.$on('事件名',方法)
- 传输数据:props
- 触发自定义事件
this.$emit('事件名',数据)
- 解绑自定义事件
this.$off('事件名')
父组件:
<!--传一个函数类型的props数据-->
<School :getSchoolName="getSchoolName"/>
<!-- 通过父组件给子组件绑定一个自定义事件实现子给父传递数据(第一种写法,使用@或v-on) -->
<Student @demo="m1"/>
<!-- 通过父组件给子组件绑定一个自定义事件实现子给父传递数据(第二种写法,使用ref) -->
<Student ref="student" @click.native="show"/>
<script>
import Student from './components/Student'
import School from './components/School'
export default {
name:'App',
components:{
School,Student},
data() {
return {
msg:'你好啊!',
studentName:''
}
},
methods: {
getSchoolName(name){
console.log('App收到了学校名:',name)
},
m1(){
console.log('demo事件被触发了!')
},
show(){
alert(123)
}
},
mounted() {
this.$refs.student.$on('demo',this.m1) // 绑定自定义事件
// this.$refs.student.$once('demo',this.m1) // 绑定自定义事件(一次性)
},
}
</script>
子组件:
<h2>当前求和为:{
{
number}}</h2>
<button @click="add">点我number++</button>
<button @click="unbind">解绑自定义事件</button>
<button @click="death">销毁当前Student组件的实例(vc)</button>
<script>
export default {
name:'Student',
data() {
return {
name:'张三',
sex:'男',
number:0
}
},
methods: {
add(){
//父组件中的自定义事件名与数据
this.$emit('atguigu',this.name,666,888,900)
// this.$emit('demo')
// this.$emit('click')
},
unbind(){
// 解绑
this.$off('atguigu') //解绑一个自定义事件
// this.$off(['atguigu','demo']) //解绑多个自定义事件
// this.$off() //解绑所有的自定义事件
},
death(){
// 销毁了当前Student组件的实例,销毁后所有Student实例的自定义事件全都不奏效
this.$destroy()
}
},
}
</script>
11.全局事件总线
一种可以在任意组件间通信的方式,本质上就是一个对象,它必须满足以下条件:
- 所有的组件对象都必须能看见他
- 这个对象必须能够使用
$on
$emit
$off
方法去绑定、触发和解绑事件
使用步骤:
- 定义全局事件总线在main.js
new Vue({
...
beforeCreate() {
Vue.prototype.$bus = this // 安装全局事件总线,$bus 就是当前应用的 vm
},
...
})
- 使用事件总线
a:接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身
export default {
methods(){
demo(data){
...}
}
...
mounted() {
this.$bus.$on('xxx',this.demo)
}
}
b:提供数据:this.$bus.$emit('xxx',data)
- 在beforeDestroy钩子中,用$off()去解绑当前组件所用到的事件
12.$nextTick
这是一个生命周期钩子
this.$nextTick(回调函数)在下一次DOM更新结束后执行其指定的回调
什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行
this.$nextTick(function () {
this.$refs.inputTitle.focus();
});
13.过渡与动画
待补充
14.配置代理
解决跨域问题:
- cors
- jsonp
- 配置代理服务器:本身与代理服务器端口号相同,代理服务器与服务器交互不存在跨域问题因为请求方式不同。
如何配置代理服务器:
配置文件vue.config.js:
方法一:
module.exports = {
devServer:{
proxy:"http://localhost:5000"
}
}
说明
- 优点:配置简单,请求资源时直接发给前端(8080)即可
- 缺点:不能配置多个代理,不能灵活的控制请求是否走代理
- 工作方式:若按照上述配置代理,当请求了前端不存在的资源时,才会将请求会转发给服务器 (优先匹配前端资源)
方法二:
module.exports = {
pages: {
index: {
entry: 'src/main.js',
},
},
lintOnSave:false,
//开启代理服务器(方式二)
devServer: {
proxy: {
'/api1': {
//请求前缀
target: 'http://localhost:5000',
pathRewrite:{
'^/api1':''},//重写,把api1去掉
// ws: true, //用于支持websocket,默认为true
// changeOrigin: true //用于控制请求头中的host值,默认值为true
},
'/api2': {
target: 'http://localhost:5001',
pathRewrite:{
'^/api2':''},
}
}
}
}
15.插槽
插槽:让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式,
适用于 父组件 ===> 子组件
分类:默认插槽、具名插槽、作用域插槽
默认插槽:
父组件:
<Category title="美食" >
<!-- 自己写结构,插进去-->
<img src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg" alt="">
</Category>
子组件:
<div class="category">
<h3>{
{ title }}分类</h3>
<!-- 定义一个插槽(挖个坑,等着组件的使用者进行填充) -->
<slot>我是一些默认值,当使用者没有传递具体结构时,我会出现</slot>
</div>
具名插槽:
父组件:
<Category title="美食" >
<img slot="conter" src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg" alt="">
<a slot="footer" href="http://www.atguigu.com">更多美食</a>
</Category>
子组件:
<div class="category">
<h3>{
{title}}分类</h3>
<!-- 定义一个插槽(挖个坑,等着组件的使用者进行填充) -->
<slot name="center">我是一些默认值,当使用者没有传递具体结构时,我会出现1</slot>
<slot name="footer">我是一些默认值,当使用者没有传递具体结构时,我会出现2</slot>
</div>
</template>
作用域插槽:数据由子组件决定,结构由父组件决定。
父组件:
<Category title="游戏">
<template scope="atguigu">
<ul>
<li v-for="(g,index) in atguigu.games" :key="index">{
{
g}}</li>
</ul>
</template>
</Category>
子组件:
<div class="category">
<h3>{
{
title}}分类</h3>
<slot :games="games" msg="hello">我是默认的一些内容</slot>
</div>