<template>
<div>
{{a}} --- {{b}}
</div>
<div>
123
</div>
</template>
interface ComponentOptions {
provide?: object | ((this: ComponentPublicInstance) => object)
}
provide
和 inject
通常成对一起使用,使一个祖先组件作为其后代组件的依赖注入方,无论这个组件的层级有多深都可以注入成功,只要他们处于同一条组件链上。
这个 provide
选项应当是一个对象或是返回一个对象的函数。这个对象包含了可注入其后代组件的属性。你可以在这个对象中使用 Symbol 类型的值作为 key。
基本使用方式
const s = Symbol()
export default {
provide: {
foo: 'foo',
[s]: 'bar'
}
}
export default {
data() {
return {
msg: 'foo'
}
}
provide() {
return {
//所供给的 msg 将不会是响应式的
msg: this.msg
}
}
}
interface ComponentOptions {
inject?: ArrayInjectOptions | ObjectInjectOptions
}
type ArrayInjectOptions = string[]
type ObjectInjectOptions = {
[key: string | symbol]:
| string
| symbol
| { from?: string | symbol; default?: any }
}
一个字符串数组
一个对象,其key名就是在当前组件中的本地绑定名称,而它的值应该是以下两种之一
如果没有供给相匹配的属性、也没有提供默认值,那么注入的属性将为 undefined。
请注意,注入绑定并非响应式的。这是有意为之的一个设计。如果要注入的值是一个响应式对象,那么这个对象上的属性将会保留响应性。
基本使用方式
export default{
inject:['foo'],
created(){
console.log(this.foo)
}
}
使用注入的值作为props的默认值
const child= {
inject: ['foo'],
props: {
bar:{
default(){
return this.foo
}
}
}
}
使用注入的值作为data
const Child={
inject:['foo'],
data(){
return{
bar:this.foo
}
}
}
注入项可以选择是否带有默认值
const Child={
inject:{
foo:{default:'foo'}
}
}
如果需要从不同名字的属性中注入,请使用 from 指明来源
const Child={
inject:{
foo:{
from:'bar',
default:'foo'
}
}
}
和props 默认值类似,对于非原始数据类型的值,你需要使用使用工厂函数
const Child = {
inject: {
foo: {
from: 'bar',
default: () => [1, 2, 3]
}
}
}
<script>
export default {
name:'export',
data(){
return{
....
}
}
......
}
</script>
存储在data里的属性,会自动的拥有监听器Observer,当发生变化时,会自动更新页面的数据
<script>
export default{
name:"Hellovue",
//data一定要使用闭包的形式,所以用函数形式而不用对象形式data:{}
data(){
return{
}
}
}
</script>
this.$set(this.obj,”a”,2) //给响应式对象动态添加属性
this.$delete(this.obj,”a”)//给响应式对象动态删除属性
this.util.defineRective(this.obj,”a”,2)//给非响应式对象动态添加属性
<h3 ref=“h3“>哈哈哈</h3> //ref是一个dom的标识符
this.$refs.h3 //vue里面获取dom元素
扩展:
为什么data是一个函数?
Vue2.0中如何检测数组变化
var vm=new Vue({
data:{a:1}
methods:{
plus:function(){
this.a++
}
}
})
vm.plus()
vm.a //2
import BasicChild1 from '../components/Basic/BasicChild1.vue'
components:{
BasicChild1
}
<basic-child1></basic-child1>
注: 全局注册组件Vue.component可以直接在组件中使用;子组件的作用是用来复用,去耦合的。
计算属性被混入到Vue实例中。所有getter和setter的this上下文自动地绑定为vue实例
注意事项:不应该使用箭头函数来定义计算属性函数例如 aDouble:()=>this.a*2。理由是箭头函数绑定了父级作用域的上下文。所以this将不会按照期望指向vue实例,this.a将是undefined。
案列
<!-- 计算属性模糊查询 -->
<div id="app">
<input type="text" v-model="mytext"/>
</div>
<ul>
<li v-for="data in datalistcom" :key="data">{{data}}</li>
</ul>
<script type="text/javascript">
var vm=new Vue({
el:'#app',
data:{
datalist:['aaa','bbb','ccc','ddd','aa','a','cc','dd'],
mytext:''
},
computed:{
datalistcom(){
return this.datalist.filter(item=>item.indexOf(this.mytext)>-1)
}
}
})
</script>
计算属性基于响应式依赖进行缓存,依赖的值未发生变化,它调用的就是上一次计算缓存中的数据,因此提高了程序的性能,而methods中每调用一次就会重新计算一次,为了进行不必要的资源消耗,就会选择用计算属性
第一次展示组件的时候马上执行一次方法,执行后结果存入缓存中。后续之后当依赖数据发生改变,才会再次执行方法。
扩展: 计算属性和监听属性的区别
var vm=new Vue({
data:{a:1,b:2,c:3},
watch:{
a:function(val,oldVal){
console.log('new:%s,old:%s',val,oldVal)
}
}
})
vm.a=2 //new:2 old:1
<body>
<div id="app">
<p>单价:<input type="text" v-model="price"/></p>
<p>数量:<input type="text" v-model="number"></p>
<p>计算金额:{{sum}}</p>
</div>
</body>
<script type="text/javascript">
var vm=new Vue({
el:'#app',
data:{
price:100,
number:1,
sum:0
},
//监听某一个值或者状态发生变化,变化就会触发watch
watch:{
//方法名和监听的参数名要一致
price(){
console.log(this.price)
if(this.price*this.number<1000&&this.price*this.number>0){
this.sum=this.price*this.number+100
}else{
this.sum=this.prthis.sum=this.price*this.numberice*this.number
}
},
number(){
console.log(this.price)
if(this.price*this.number<1000&&this.price*this.number>0){
this.sum=this.price*this.number+100
}
else{
}
}
}
})
</script>
1.函数形式
Watch:{
a(val1,val2){
// 方法中的固定参数
//参数1 数据改变后的值
//参数2 数据改变前的值
}
}//此例中,侦听数据a,a发生改变时,马上执行这个方法
2.对象形式
//注册成一个对象
watch(){
a:{
handler(val1,val2){
console.log('a发生了改变')
console.log(val1+'-----'+val2)
}
}
}
方法:
handler 方法名是固定的,必须是handler,与简写时注册方法的效果相同
属性:
immediate 默认值是false 当值为true 第一次展示页面会马上执行一次侦听的方法。
deep(只有在对象形式中才能使用)深度监听,监听对象内部的变化,默认值为false,需要改成true。
// 用户在网页一边输入内容,一边网页自己查找
watch:{
kws(){
this.search()
}
}
methods:{
search(){
clearTimeout(this.timer)
this.timer=setTimeout(()=>{
if(this.kws.trim()!==""){
console.log(`搜索和${this.kws}相关的内容...`)
}
},500)
}
}
props:{
name:{
type:String
},
width:{
type:String
},
url:{
type:String
},
},
props:[name,width,url]
父子组件渲染时的通信
产生问题的原因
父组件异步获取后台数据,这时候加载渲染数据生命周期已经走完,只能更新数据,触发更新渲染生命周期,所以子组件加载时,永远只能拿到父组件的初始数据,拿不到更新后的数据,但是,props是可以等的,页面最后是可以拿到异步的数据渲染的。
如何解决子组件加载获取不到父组件异步获取数据的问题。
总结:【20230827】子组件props如果绑定动态数据,默认只在加载时传递,也就是说只传一次。props绑定视图层,可以传多次。父组件created赋值同步数据不会触发updated,同步数据可以在created时就传递给子组件。父组件赋值异步数据,触发update,子组件也会在update才能拿到数据,所以加载时只能拿到父组件的初始化数据。
1.全局创建(脚手架在main.js创建)
Vue.filter('moneyFilter',function(oldVal){
return `¥${oldval.toFixed(2)}`
})
2.在组件对象中创建
filters:{
uppercase:function(str){
return str.toUpperCase()
}
}
3.过滤器的使用
价格:{{price|moneyFilter}} //|是管道
<script>
Vue.filter('moneyFilter',function(oldVal,type){
return `${type||'¥'}${oldVal.toFixed(2)}`
})
</script>
<body>
<div id="app">
<h1>价格是:{{price1||moneyFilter('$')}}</h1>
<h1>价格是:{{price2||moneyFilter('¥')}}</h1>
</div>
<script>
new Vue({
el:"#app",
data:{
price1:12.5
price2:17.5
}
})
</script>
</body>
<template>
<div class="demo05">
<h2>过滤器</h2>
<h3>{{sex|gender}}</h3>
<h3>{{salary|format(5)}}</h3>
</div>
</template>
<script>
export default{
data(){
return{
sex:1,
salary:10000
}
},
filters:{
gender:function(value){
return(value==1&&"男) || (value == 0 && "女")
},
format:function(value,n){
return value.toFixed(n)
}
}
}
</script>
Vue.filter('gender',function(value){
return(value==1&&"男) || (value == 0 && "女")
})
Vue.filter('format',function(value){
return value.toFixed(n)
})
1.封装方法1: 把以上代码封装到一个公共js文件中,再引入到全局文件main.js
const filters={
gender:function(value){
return(value==1&&"男) || (value == 0 && "女")
},
format:fucntion(value,n){
return value.toFixed(n)
}
}
export default filters
//main.js
import filters from './filter'
Object.keys(filters).forEach(val=>{
Vue.filters(val,filters[val])
})
2.封装方法2:直接在index.js中引入vue
除了默认设置的核心指定(v-model和v-show),Vue也允许注册自定义指令,注意,在vue2.0里面,代码复用的主要形式和抽象是组件—然而,有的情况下,你仍然需要对 纯DOM元素进行底层操作,这时候就需要用到自定义指令。
vue2.x 指令里提供的几个钩子函数
bind:指令被绑到元素上的时候使用,只会执行一次
inserted:绑定指令的元素被添加到父元素上的时候使用,只会执行一次
update:所在组建的VNode更新时调用,但是可能发生在其子VNode更新之前,也就是说元素更新, 但子元素尚未更新, 将调用此钩子( 自定义指令所在组件更新时执行, 但是不保证更新完成 ) —> 和自定义所在组件有关
componentUpdated:组件和子级更新后执行( 自定义指令所在组件更新完成, 且子组件也完成更新 ).
unbind:解绑(销毁) 自定义指令所在的dom销毁时执行,只调用一次.
钩子函数提供的参数
binding.arg: String,属性名。例如v-my-directive:foo中,属性名为"foo"
binding.modifiers:Object, 包含所有修饰符的一个对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }。
binding.name:String, 指令名
binding.rawNmae:String, 完整指令名,例如 v-my-directive:foo.bar=“1 + 1” 中, 完整指令名就是 v-my-directive:foo.bar=“1 + 1”。
binding.value:Any, 指令绑定的当前值,例如:v-my-directive=“1 + 1” 中,绑定值为 2。
binding.expression:String, 解析的哪一个值、表达式。例如 v-my-directive=“1 + 1” 中,表达式为 “1 + 1”。
binding.oldValue:Any, 指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。
binding.oldArg:Any, 指令属性名的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。
案列1: 全局方法创建一个叫做v-focus的指令
//全局创建指令
//创建指令不加v-前缀
//大括号{}里写的是指令中提供的钩子函数,方法体写在钩子函数里
Vue.directive('focus',{
inserted(el){
el.focus()
}
})
//使用
//使用指令时,要加上v-
<input v-focus>
//局部创建指令
directives:{
fontSize:{
bind :function(el,binding){// binding.value 绑定的当前值
el.style.fontSize = paseInt(binding.value) + 'px'
}
}
//使用
//使用指令时,要加上v-
<div v-fontSize="12"></div>
export default{
functional:true,
props:{
data:{
type:Object,
default:()=>{}
}
},
methods:{
getName(){
// 需要this来调用的属性 可需要context来调用
return context.props.data.name
}
}
}
默认的插值是双大括号{{}}。但有时我们会有需求更改这个插值的形式。
delimiters : [ '${' , '}' ]
现在我们的插值形式就变成了${}。代替了{{ }}
// 子组件
<template>
<el-dialog
:visible="visible"
@close="onClose"
>
</el-dialog>
</template>
<script>
export default {
name: 'HDialog',
model: {
prop: 'visible',
event: 'closeModal'
},
props: {
visible: {
type: Boolean,
default() {
return false
}
}
},
methods: {
// 点击关闭抽屉层
onClose() {
this.$emit('close')
// 修改model中的值
this.$emit('closeModal', false)
}
}
}
</script>
// 父组件
<h-dialog v-model="visible"></h-dialog>
inheritAttrs: true
inheritAttrs: true
一个包含组件选项对象的数组,这些选项都被混入到当前组件的实例中
类型
interface ComponentOptions{
mixins?:ComponentOptions[]
}
mixin选项接受一个mixin对象数组。这些mixin对象可以像普通的实例对象一样包含实例选项,它们将使用一定的选项合并逻辑与最终的选项进行合并。举例来说,如果你的 mixin 包含了一个 created 钩子,而组件自身也有一个,那么这两个函数都会被调用。Mixin 钩子的调用顺序与提供它们的选项顺序相同,且会在组件自身的钩子前被调用。
const mixin = {
created() {
console.log(1)
}
}
export default{
created() {
console.log(2)
},
mixins: [mixin]
})
// => 1
// => 2
一些方法一类的东西
把这些方法封装在一个对象中(一个js文件)
用混入的方式,把这个对象混入到每个组件中
那么,这个对象中的属性就可以混入到组件的vue对象中
既然,要混入到vue对象中,所以这个对象中的属性只能是vue实例中的属性
注意(mixin不再推荐):在 Vue 2 中,mixins 是创建可重用组件逻辑的主要方式。尽管在 Vue 3 中保留了 mixins 支持,但对于组件间的逻辑复用,使用组合式 API 的组合式函数是现在更推荐的方式。
1.新建混入器对象
export default{
data(){
return{
a:5,
b:7
}
},
methods:{
fn(){
alert(123)
}
}
}
2.将混入器对象文件引入到组件中
import mix from '../../mixin.js'
3.在vue对象的mixins属性中注册混入对象
export default{
name:'mixin',
//注册混入对象
mixin:[mix]
}
4.在视图层使用注册的混入对象
<template>
<div>
<span>{{a}}{{b}}</span>
<button @click="fn">弹出</button>
</div>
</template>
注意
当前组件和混入对象产生冲突时,冲突的内容如果非此即彼,那么就使用当前组件的
–
propsData 不是和属性有关,它用在全局扩展时进行传递数据。 类型:{ [key: string]: any }。 限制:只用于 new 创建的实例中。 详细:创建实例时传递 props。主要作用是方便测试。首先这个使用率并不高,因为后面会有vuex,他的作用就是单页面应用中保持状态和数据的,本机就是在全局扩展里面传递参数propsData。
举例
<div id="app">
</div>
<template id="eg">
<div>
<h5>这个是使用extend创建出来的组件</h5>
</div>
</template>
<script src="./Vuejs/vue2-6-12.js"></script>
<script>
// 通过extend扩展一个新的组件
var myextend = Vue.extend({
template:"#eg"
})
// 实例化对象
new myextend().$mount("#app");
</script>
我们可以看到这是使用extend方法创建组件,但是在使用这个方法的时候,我们是不容易传递数据的,因此这里引入了propsData。
<div id="app">
</div>
<template id="eg">
<div>
<h5>这个是使用extend创建出来的组件</h5>
<h6>{{news}}</h6>
</div>
</template>
<script src="./Vuejs/vue2-6-12.js"></script>
<script>
// 通过extend扩展一个新的组件
var myextend = Vue.extend({
template:"#eg",
props:['news']//需要通过props来接收,接收的名称得和propsData的key相同
})
// 实例化对象
new myextend({propsData:{news:"这个就是propsData传递的数据"}}).$mount("#app");
</script>