赞
踩
vue
的单文件组件是一种以.vue
结尾的特殊的文件格式,vue
工程化项目中不再有html
的页面的概
念,一切皆为一个vue
的单文件组件。它是不能被浏览器独立运行的,需要通过编译工具,编译为能被
浏览器解析的 js 文件执行。
数据的变化驱动视图的自动更新,是vue
的一个特性。以前要想修改视图的数据,是通过js
或者
jquery
操作 dom
,改变dom
中的内容实现。现在只修改数据,数据改变,vue
会自动渲染 dom
更新,而不用我们再来操作 dom
。
所以我们在vue
项目中相比以前的jquery
项目中要有思想上的改变:从以前操作 dom
的思想转换为操作数据。
参考 vue 官网 https://v2.cn.vuejs.org/v2/guide/
标准的 vue 文件结构分为三个部分,模板(template)
、js(script)
、样式(style)
基本结构如下:
<template> <!-- 有且只有一个根节点 --> <div></div> </template> <script> export default { // 数据 data() { return { obj: '', arr: [] }; }, // 生命周期 created() { this.init(); }, mounted() { }, // 方法 methods: { init() { //... } } }; </script> <!-- scoped 声明私有样式:只在该组件下起作用 --> <!-- lang="scss" 声明css扩展语言 --> <style lang="scss" scoped></style>
其中模板只能包含一个父节点,即模板内部有且只有一个根结点。这是 vue
单文件组件规范。
template
类似于html
部分。
vue
通常用 es6
来写,用export default
导出,其下面可以包含数据data
,生命周期(mounted等)
,方法(methods)
等。
data
:模版中用到的数据都来自data,数据驱动视图也就代表修改data中的数据,会引起视图的created
:在模板渲染成 html 前调用,即通常初始化某些属性值,然后再渲染成视图,调用method中的方法。mouted
:在模板渲染成 html 后调用,通常是初始化页面完成后,再对 html 的 dom 节点进行一些需要的操作,调用method中的方法。method
:用来定义 js 方法、事件等实现某些功能。script标签内部结构顺序:
mixin > components > props > data > computed > watch > filter >生命周期钩子函数 >
errorCaptured > methods
生命周期钩子函数按其执行顺序书写:
beforeCreate > created > beforeMount > mounted
> beforeUpdate > updated > activated > deactivated > beforeDestroy > destroyed
样式通过style
标签包裹,默认是影响全局的,如需定义作用域只在该组件下起作用,需在标签上加
scoped
,,也可使用css
扩展语言 scss
,默认 css
,使用扩展语言需要声明lang
属性,使用scss
语言
用于解析标签体内容。{{xxx}}
, xxx
是 js
表达式,且可以直接读取到data
中的所有属性。
例子:
<template> <!-- 插值表达式 --> <div>{{ msg }}</div> <!-- 插值表达式{{}}对JavaScript 表达式支持,例如: --> <!-- 运算 --> {{ number + 1 }} <!-- 字符串拼接 --> {{ str + 'suffi'}} <!-- 三元表达式 --> {{ gender === '0' ? '男' : '女' }} <!-- js的字符串操作 --> {{ msg.substring(0,2) }} </template> export default { data() { return { //属性名 msg: '这是一段文字', number:0, str:'字符串', gender:'0' }; } }; </script>
原样输出,替换标签内原有内容, 本质是innerText
属性的修改
<template> <div> <!-- v-text: 原样输出 替换标签内原有内容, 本质是 innerText 属性的修改 --> <div v-text="msg">msg:</div> </div> </template> <script> export default { data() { return { msg: '<h1>Hello Vue</h1>' }; } }; </script> <style lang="scss" scoped> //样式 </style>
输出:
<h1>Hello Vue</h1>
更新元素的innerHTML
,本质是innerHTML
属性的修改。 注意:内容按普通 HTML
插入 - 不会作为
Vue
模板进行编译。
例:
<template>
<div>
<!-- v-html: 把内容解析成HTML元素, 覆盖原有内容。 -->
<div v-html="msg">msg:</div>
</div>
</template>
<script>
export default {
data() {
return {
msg: '<h1>Hello Vue</h1>'
};
}
};
</script>
输出:
Hello Vue
v-bind
可简写为 : 动态地绑定一个或多个 属性。在绑定 ``class 或
style ```属性 时,支持其它类型的值,如数组或对象。
例:
<!-- 绑定属性名imageSrc -->
<img v-bind:src="imageSrc">
<!-- 简写 -->
<img :src="imageSrc">
例:
当isActive
或者 hasError
变化时,class
列表将相应地更新。例如,如果 hasError
的值为
true
,class
列表将变为"static active text-danger" 。
<template>
<!-- 动态:class可以与普通class共存 -->
<div class="static" :class="{ active: isActive, 'text-danger': hasError }">一段
文字</div>
</template>
<script>
export default {
data() {
return {
isActive: true,
hasError: false
};
}
};
</script>
渲染结果为:
<div class="static active">一段文字</div>
例:
<template>
<!-- :class绑定一个数组 -->
<div :class="[activeClass, errorClass]">一段文字</div>
</template>
<script>
export default {
data() {
return {
activeClass: 'active',
errorClass: 'text-danger'
};
}
};
</script>
渲染结果为:
<div class="active text-danger"></div>
如果你也想根据条件切换列表中的 class,可以用三元表达式:
<!-- 当数据isActive为真时,样式activeClass才会被应用。这样将始终添加 errorClass -->
<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>
class有多个条件时,这样写有些繁琐。所以在数组语法中也可以使用对象语法:
<div v-bind:class="[{ activeClass: isActive }, errorClass]"></div>
对象语法:
v-bind:style
的对象语法十分直观—看着非常像CSS
,但其实是一个JavaScript
对象。 CSS
名可以用驼峰式 或短横线分隔 (kebab-case,短横线分隔要用引号括起来) 来命名:
例子:
<template>
<!-- 绑定属性activeColor值和fontSize值,渲染样式-->
<div :style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
</template>
<script>
export default {
data() {
return {
activeColor: 'red',
fontSize: '20'
};
}
};
</script>
直接绑定到一个样式对象通常更好,这会让模板更清晰:
<template> <!-- 绑定styleObject对象,渲染样式--> <div :style="styleObject"></div> </template> <script> export default { data() { return { styleObject: { color: 'red', fontSize: '14px' }, styleObjecttwo: { background: '#ccc' } }; } }; </script>
<div v-bind:style="[styleObject,styleObjecttwo]"></div>
vue
会去实例v-on:事件名.once="函数名"
;@事件名='函数名($event)'
来获取事件对象用在普通元素上时,用于监听原生Dom事件。用在自定义元素组件上时,也可以监听子组件触发
的自定义事件。
事件修饰符 | 示例 | 示例说明 |
---|---|---|
.stop | ```@click.stop`` | 阻止单击事件冒泡 |
.prvent | @submit.prevent | 阻止事件的默认行为 |
.once | @click.once | 规定该事件只会触发一次 |
事件修饰符 | 示例 | 说明 |
---|---|---|
.enter | @click.enter | 回车键 |
.delete | @click.delete | delete键和退格键 |
.esc | @click.esc | esc键 |
.space | @click.space | 空格键 |
例 @click.once
修饰符事件只会触发一次:
<template> <div> <el-button type="primary" @click="clickOnFn">我是按钮-简写</el-button> <el-button type="primary" @click.once="clickOneFn">我是按钮-.once</el-button> </div> </template> <script> export default { data() { return { msg: '' }; }, methods: { //绑定事件 clickOnFn() { this.$message({ message: '按钮', type: 'success' }); }, //绑定事件,只调用一次 clickOneFn() { this.$message({ message: '只调用一次', type: 'success' }); } } }; </script>
在表单控件或者组件上创建双向绑定。
例:
<template> <div> <!-- 视图发生改变→对应的数据发生改变:用户在输入框里输入的内容会被msg变量接下来,同时也将 动态更新在p标签的内容里。--> <input v-model="msg" /> <p>{{ msg }}</p> </div> </template> <script> export default { data() { return { msg: '' }; } }; </script>
根据表达式的真假值,切换元素的 display 的 CSS
属性。即控制显示隐藏
例子:
<template>
<!-- 绑定v-show指令,控制div的显示隐藏-->
<div v-show="show">v-show指令</div>
</template>
<script>
export default {
data() {
return {
show: true //tue显示;false隐藏
};
}
};
</script>
v-if根据表达式的值的是否为真,来有条件地渲染元素。本质是通过操作dom
元素来切换显示状态。
v-for优先级比v-if高,不建议两者一起使用。
例子:
<template>
<!-- 绑定v-if指令,控制div的显示隐藏-->
<div v-if="show">v-if指令</div>
</template>
<script>
export default {
data() {
return {
show: true //tue显示;false隐藏
};
}
};
</script>
v-if
是真正的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销v-if
也是惰性的:如果在初始渲染时条件为假,则什么也不做—直到条件第一次变为真时,才会v-show
就简单得多—不管初始条件是什么,元素总是会被渲染,并且只是简单地基于CSS
进行切换。v-if
有更高的切换开销,而 v-show
有更高的初始渲染开销。因此,如果需要非常频v-show
较好;如果在运行时条件很少改变,或控制的是一个组件,需要重新编译,则使用v-if
较好。为 v-if 或者 v-else-if 添加“else 块”。
v-else和v-else-if的前一兄弟元素必须有 v-if 或 v-else-if
<div v-if="type === 'a'">
a
</div>
<div v-else-if="type === 'b'">
b
</div>
<div v-else-if="type === 'c'">
c
</div>
<div v-else>
not a/b/c
</div>
v-for
基于源数据多次渲染元素或模板块,v-for
指令中可以使用数组、对象。
数组语法: (item, index) in items
其中items
为源数据数组,item
为数组元素别名,index
为索引具有唯一性
例子:
<div> <!-- 遍历对象 --> <div v-for="(val, key, index) in object" :key="index">{{ val }}{{ key }}{{ index }}</div> </div> </template> <script> export default { data() { return { object: { name: '张三', age: '20' } }; } }; </script>
遍历对象渲染结果为:
张三name0
20age1
key 为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需
要为每项提供一个唯一 key ,这个key 值必需要有且具有唯一性,通常建议使用id来作为key值,
不建议使用索引。
计算属性可用于快速计算视图中显示的属性。这些计算将被缓存,并且只在需要时更新。所以,对于任 何复杂计算,都可以使用计算属性。
例计算数量的总数:
<template> <div> <!-- 使用计算属性:当做一个属性来使用 --> <div>总数值:{{ count }}</div> </div> </template> <script> export default { data() { return { list: [ { name: '数量01', count: 10 }, { name: '数量02', count: 20 }, { name: '数量03', count: 30 } ] }; }, //在computed节点中定义方法count computed: { // 计算数量的总数 count() { let countNum = 0; this.list.forEach(item => { countNum += item.count; }); return countNum; } }, mounted() { console.log(this.count); } }; </script>
watch用于监听data里面的数据是否被修改,一旦修改就可以执行一些其他的操作。
immediate:true
属性。<template> <div> <!-- 输入框num值改变,执行watch监听。改变numDouble变量--> <el-input v-model="num" placeholder="请输入内容"></el-input> <h2>监听num并*2:{{ numDouble }}</h2> </div> </template> <script> export default { data() { return { num: 0, numDouble: 0, obj: { a: 1, b: 2 } }; }, watch: { //监听num,函数第一个参数是变化后的新值,第二个参数是旧值 num: function(newVal, oldVal) { this.numDouble = newVal*2; }, //对象 obj: { handler: function(newVal, oldVal) { //... }, deep: true //深度监听 }, //对象属性,使用单引号包裹 'obj.a': { handler: function(newVal, oldVal) { //... }, immediate: true//控制侦听器是否自动触发,即首次进入页面就监听 } }, methods: {} }; </script>
vue
中的一个特性,用于对文本进行格式化。在不改变原数据的前提下,对数据进行一定 程度的处理。<!-- 在{{ }}插值语法中, 格式:{{值 | 过滤器的名称}} -->
{{ message | capitalize }}
<!-- 在v-bind中, 格式:v-bind:id="值 | 过滤器的名称" -->
<div v-bind:id="rawId | formatId"></div>
<!-- 过滤器可以串联,管道符 | 隔开。 将filterA 的结果传递到 filterB 中-->
{{ message | filterA | filterB }}
<!-- 过滤器是 JavaScript 函数,因此可以接收参数 格式:{{ 值 | filter1(参数) |
fliter2(参数) }}-->
{{ message | filterA('arg1', arg2) }}
在开发中,需要用到过滤器的地方有很多,比如单位转换、数字打点、文本格式化、时间格式化之类的 等
例转换时间戳、个位补零:
<template> <div> <!-- 局部过滤器 --> <!-- 要过滤的数据,永远是第一个参数;通过filter函数,传递的参数,依次排在后面 --> <div>日期转换:{{ startDate | dateFilter }}</div> <div>日期转换:{{ startDate | dateFilter('YYYY-MM-DD') }}</div> <!-- 全局过滤器 --> <p>个位补零</p> <p v-for="(num, index) in dateList" :key="index">{{ num | fillZero }}</p> </div> </template> <script> import moment from 'moment'; export default { data() { return { startDate: new Date(), dateList: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] }; }, filters: { //处理日期格式 dateFilter: function(time, pattern) { if (!pattern) { pattern = 'YYYY-MM-DD HH:mm:ss';//默认格式 } return moment(time).format(pattern); } } }; </script>
全局过滤器经常用在数据修饰上,通常我们把处理函数给抽离出去,统一放在一个 .js 文件中。
filter.js
文件//个位补零
function fillZero(num) {
return num < 10 ? `0${num}` : num;
}
//导出过滤器方法
export default {fillZero};
main.js
中 引入注册 filter.js
文件//引入过滤器
import filters from './utils/filter.js'
//注册过滤器,导入的是一个对象,所以使用Object.keys()方法,得到一个由key组成的数组,遍历数据
让key作为全局过滤器的名字
Object.keys(filters).forEach(key => {
Vue.filter(key, filters[key]);
});
Vue
在实际开发过程中会存在重复使用的 UI 结构,我们可以将类似的 UI 结构提取出来封装成组件。
新建一个 vue 文件做为子组件。
1)import 导入
<script>
import ChildPage from './child-page';
</script>
2)components中注册
<script>
import ChildPage from './child-page';
export default {
components: {
ChildPage
}
}
</script>
3)template中以标签形式使用
<template>
<div>
<!-- 把组件名转换为中划线分割的方式使用,与前端开发规范保持一致 -->
<child-page></child-page>
</div>
</template>
<script>
import ChildPage from './child-page';
export default {
components: {
ChildPage
}
</script>
1)父组件传值给子组件
1.通过 props传递
props
可以是数组或对象,用于接收来自父组件的数据。对象允许配置高级选项,如类型检测、自定义 验证和设置默认值。
实际工作开发中,我们都要求统一使用对象的形式。在传递的数值类型不正确时, vue 会报错方便我们 定位错误。 以对象形式接收参数的配置选项:
例父组件传递年龄age变量到子组件,其中age为子组件接收的名称:
<template>
<div>
<!-- 我是父组件 -->
<child-page :age="10"></child-page>
</div>
</template>
<script>
import ChildPage from './child-page';
export default {
components: {
ChildPage
}
}
</script>
子组件通过props接收age并渲染:
<template> <div> <!-- 我是子组件 --> <div>{{ age }}</div> </div> </template> <script> export default { props: { // 检测类型 + 验证 age: { type: [String, Number],//支持多种type类型 default: 0,//默认值和类型保持一致 required: true,//必填项 validator: function(value) {//自定义验证函数 return value >= 0; } }, propA: { type: Number,//单个type类型 default: 0 }, propB: { type: String, default: '' }, propC: { type: Array, default() { return []; } }, propD: { type: Object, default() { return {}; } }, propE: { type: Boolean, default: false } } }; </script>
传递静态Prop,可以像这样给 prop
传入一个静态的值:
<blog-post title="My journey with Vue"></blog-post>
所有的 prop 都使其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件 中,但是反过来则不行(子组件不能直接修改父组件传过来的值)。这样会防止从子组件意外变更父级 组件的状态,从而导致你的应用的数据流向难以理解。
每次父级组件发生变更时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一 个子组件内部改变 prop。如果你这样做了, Vue 会在浏览器的控制台中发出警告。
例 prop 以一种原始的值传入且需要进行转换。在这种情况下,最好使用这个 prop 的值来定义一个 计算属性:
父组件
<template> <div> <!-- 我是父组件 --> <child-page :size="size"></child-page> </div> </template> <script> import ChildPage from './child-page'; export default { components: { ChildPage }, data() { return { size: 'M' }; } }; </script>
子组件
<template> <div> <!-- 插值表达式使用normalizedSize属性渲染 --> <div>{{ normalizedSize }}</div> </div> </template> <script> export default { //props接收 props: { size: { type: String, default: null } }, //在computed节点中定义方法normalizedSize computed: { normalizedSize: function() { return this.size.toLowerCase(); //转换成小写 } } }; </script>
2、父组件通过ref调用子组件方法传值 this.$refs
例父组件:
<template> <div> <!-- 在子组件上绑定ref --> <child-page ref="childRef"></child-page> <el-button type="primary" @click="getChildInit()">点击按钮调用子组件方法</elbutton> </div> </template> <script> import ChildPage from './child-page'; export default { components: { ChildPage }, data() { return {}; }, created() {}, mounted() {}, methods: { //调用子组件initDate方法 getChildInit() { let val = "父组件传递值"; this.$refs.childRef.initDate(val); } } }; </script>
子组件
<template> <!-- 我是子组件 --> <div></div> </template> <script> export default { data() { return {}; }, created() {}, mounted() {}, methods: { //父组件调用 initDate(val) { //val是:父组件传递值 this.$message({ message: '我是子组件的方法', type: 'success' }); } } }; </script>
2)子组件传值给父组件
这里需要用到 vue 的一个实例方法** e m i t ∗ ∗ , ∗ ∗ emit**,** emit∗∗,∗∗emit**通常用于子组件调用父组件方法,实现子组件主动与 父组件进行通讯传值。
语法:[vm.$emit( eventName, …args)]
参数: {string} eventName 事件名 […args] 参数 触发当前实例上的事件。附加参数都会传给监听器回调。
例
<template> <div> <!-- 我是子组件 --> <button type="default" @click="toParentData">触发事件传值给父组件</button> </div> </template> <script> export default { data() { return { title: '我是子组件' }; }, methods: { // 子组件调用vm.$emit方法 // 注意:这里的getData是父组件中绑定的事件名 toParentData() { this.$emit('getData', this.title); } } }; </script>
<template> <div> <!-- 父组件,通过@绑定了一个getChildData事件来监听子组件的触发事件 --> <child-page @getData="getChildrenData"></child-page> </div> </template> <script> import ChildPage from './child-page'; export default { components: { ChildPage }, data() { return { title: '' }; }, methods: { // 用自定义事件来接收子组件传的值 getChildrenData(e) { this.title = e; } } }; </script>
插槽是子组件中的提供给父组件使用的一个占位符,用 表示,父组件可以在这个占位符中填充任何模板 代码,如 HTML、组件等,填充的内容会替换子组件的标签。
1)默认插槽:在子组件内使用 slot 标签占位,在slot标签内的内容为默认内容,如果不传值,则显示默 认内容。
例:父组件默认展示子组件的内容,填充内容后替换默认内容
<!-- 我是父组件 -->
<template>
<!-- 展示子组件默认内容 -->
<child-page></child-page>
<!-- 展示父组件填充内容 -->
<child-page>我替换了myslot</child-page>
</template>
<!-- 我是子组件 -->
<template>
<div>
<slot>默认显示我</slot>
</div>
</template>
2)具名插槽:一个子组件可以放多个插槽,而且可以放在不同的地方,而父组件填充内容时,可以根据 这个名字把内容填充到对应插槽中。
<!-- 具名插槽子组件 --> <template> <div class="container"> <header> <!-- 我们希望把页头放这里 --> <slot name="header"></slot> </header> <main> <!-- 我们希望把主要内容放这里 --> <slot></slot> </main> <footer> <!-- 我们希望把页脚放这里 --> <slot name="footer"></slot> </footer> </div> </template>
<!-- 我是父组件 -->
<template>
<child-page>
<!-- v-slot:子组件的name值,指定对应的插槽 -->
<template v-slot:header>
</template>
<p>主要内容</p>
<template v-slot:footer>
<p>页脚</p>
</template>
</child-page>
</template>
3)作用域插槽:子组件往插槽上绑定数据,父组件使用时可接收
<!-- 列表子组件 --> <template> <div> <ul> <li v-for="(item,index) in list" :key="item.id"> <!-- v-bind绑定数据 --> <slot :item='item'></slot> </li> </ul> </div> </template> <script> export default { data () { return { list:[ { name:'作用域插槽1', id:1 }, { name:'作用域插槽2', id:2 }, { name:'作用域插槽3', id:3 } ] } } } </script>
<!-- 我是父组件 --> <template> <div> <child-page> <!-- v-slot="变量名" 来接收子组件传递过来的数据 --> <template v-slot="slotProps"> <el-button type="primary" @click="slotFn(slotProps.item)">父组件按钮 {{slotProps.item.id}}</el-button> </template> </child-page> </div> </template> <script> import ChildPage from './child-page'; export default { components: { ChildPage }, data() { return {}; }, methods: { slotFn(row){ this.$message({ message: row, type: 'success' }); } } }; </script>
() {
return {
list:[
{
name:‘作用域插槽1’,
id:1
},
{
name:‘作用域插槽2’,
id:2
},
{
name:‘作用域插槽3’,
id:3
}
]
}
}
}
```vue <!-- 我是父组件 --> <template> <div> <child-page> <!-- v-slot="变量名" 来接收子组件传递过来的数据 --> <template v-slot="slotProps"> <el-button type="primary" @click="slotFn(slotProps.item)">父组件按钮 {{slotProps.item.id}}</el-button> </template> </child-page> </div> </template> <script> import ChildPage from './child-page'; export default { components: { ChildPage }, data() { return {}; }, methods: { slotFn(row){ this.$message({ message: row, type: 'success' }); } } }; </script>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。