TL’DR: 原因主要是重复定义了两个data导致的

other:父子组件通信、读取local storage、缩写符号、双向绑定、MVVM

一、问题截图

not defined

二、代码

<!--view层 模板-->
<div id="app" v-cloak>
    <p>{{msg}}</p>
    <div>{{info.name}}</div>
    <div>{{info.grade.level}}</div>
    <a :href="info.url">click me!</a>
</div>
<script>
    var vm = new Vue({
        el: "#app",
        data: {
            msg: "hook function"
        },
        data(){
            return{
                //请求的返回参数格式, 必须和json字符串一样
                info: {
                    name: null,
                    grade: {
                        course: null,
                        level: null
                    }
                }
            }
        }

三、解决

删掉上一个 data:{} 就行了;其实, “data() { }” 为 “data: function(){ }” 的简写,相当于重复写了两个data, 第一个没有生效;

四、关于data的写法

data 定义其实有三种方式:

// 1. Vue 实例中 data 属性
data: {
    msg: "hook function"
},
// 2. 组件化的项目中使用方法
data() {
    return{
    	msg: "hook function"
    }
}
// 3. 在大型项目中 data 会造成数据污染(data 是全局的)将 data 封装成一个函数,我们在实例化组件的时候只是调用了这个函数生成的数据副本,这就避免了数据污染
data: function(){
	var msg = "hook function";
	return msg;
}

注意, 在组件 (component) 里,data 必须声明为 有返回一个初始数据对象函数

export default{
    data(){
        return {
            msg: "hook function",
        }
    },
}

因为在组件情况下,存在多地调用同一个组件的情况。为了不让多处的组件共享同一个data对象,就规定提供 data 函数来返回一个对象;创建新实例后会调用 data 函数,返回初始数据的全新副本数据对象。

总之,组件的 data, 必须是带 return 的 data() 函数这一种形式

另外
在这里插入图片描述

Other

  1. @ 作为 v-on 指令的缩写, 表示监听事件, 用于父子组件之间方法的传递, 为组件绑定事件. 子组件不需要接收, 在子组件的 $listeners 上可以找到, 可以使用 this.$emit('xxx') 手动触发
    另外, "@/api/api"@ 等价于/src 目录的相对路径
  2. : 作为 v-bind 指令的缩写, 表示绑定参数. 用于父子组件之间值传递, 子组件中需要使用 props 接收

一、父子组件通信

ref属性
  • 被用来给元素或子组件注册引用信息(id的替代者)
  • 应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象(vc)
  • 使用方式:
    • 打标识:<h1 ref="xxx">.....</h1><School ref="xxx"></School>
    • 获取:this.$refs.xxx

具体案例

<template>
	<div>
		<h1 v-text="msg" ref="title"></h1>
		<button ref="btn" @click="showDOM">点我输出上方的DOM元素</button>
		<School ref="sch"/>
	</div>
</template>

<script>
	//引入School组件
	import School from './components/School'

	export default {
		name:'App',
		components:{School},
		data() {
			return {
				msg:'欢迎学习Vue!'
			}
		},
		methods: {
			showDOM(){
				console.log(this.$refs.title) //真实DOM元素
				console.log(this.$refs.btn) //真实DOM元素
				console.log(this.$refs.sch) //School组件的实例对象(vc)
			}
		},
	}
</script>
props配置项
  • 功能:让组件接收外部传过来的数据

  • 传递数据:<Demo name="xxx"/>

  • 接收数据:

    • 第一种方式(只接收):props:['name']
    • 第二种方式(限制类型):props:{name:String}
    • 第三种方式(限制类型、限制必要性、指定默认值)
props:{
	name:{
        type:String, //类型
        required:true, //必要性
        default:'老王' //默认值
	}
}

备注:props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制props的内容到 data 中一份,然后去修改 data 中的数据。

修改案例
  1. 父组件
// 父类向组件通信(父)    记住前面标签属性是子组件的,后面的值是父组件的
// 1. 以下是 template 标签
<button @click="openDialog">显示按钮(如:弹窗)</button>
<Child :childAttribute="parentAttribute" :childAttribute2="parentAttribute2" 
	:visibleFlag="visibleFlag " @childEvent="parentMethod(val)"></Child>
// 2. script 标签
import {Child} from "../component/Child" // 引用组件(1/2)
export default{
     component: {
     	Child, // 引用组件(2/2)
     }
     data(){
     	return{
     		visibleFlag: false, // 设置子组件可见性的参数
     		parentAttribute: {}, // 传给子组件的参数2
     		parentAttribute2: {}, // 传给子组件的参数2
     		returnBackData: {}, // 设置一个接收参数
     	}
     }
     method:{
     	openDialog(){
     		this.visibleFlag = true;
     	},
     	parentMethod(val){ // 需要在子组件使用$emit显式的调用
     		this.visibleFlag = false; // 在子组件传值回来时,将子组件(如:弹窗)的可见性设为不可见
     		this.returnBackData = val; // 接收返回参数
     	}
     }
}
  1. 子组件
// 组件向父类通信(子) 
// 1. 以下是 template 标签
<dialog v-model="visibleFlag" v-if="visibleFlag" xxxxxx>
	....<button @click="confirmBalabala">确认</button>
// 2. script 标签
export default{
	props: ["childAttribute","childAttribute2","visibleFlag"],   // 很重要!必须用这个规定用什么来接收,也就是Child标签上的属性;
	// props还有其他方式,比如规定类型的。上面是最简单最省略的。
    data(){
        return {
        	childAttribute: {},
        	childAttribute2: {},
            visibleFlag: false,
            returnBackData: {
            	balabalaba: "balabala",
            }
        }
    },
    method:{
     	confirmBalabala(){ 
     		// 使用$emit调用(事件名:也就是Child标签的属性, 要传的值)
     		this.$emit('childEvent',this.balabalaba);
     		this.visibleFlag = false; // 关闭组件可见性
    	}
    }    
}
mixin(混入)

混入 (mixin) 提供了一种非常灵活的方式, 用来 分发 Vue 组件中的可复用功能.

一个混入对象可以包含任意组件选项, 当组件使用混入对象时, 所有混入对象的选项将被 “混合” 进入该组件本身的选项.

例子:

// 定义一个混入对象
var myMixin = {
  created: function () {
    this.hello()
  },
  methods: {
    hello: function () {
      console.log('hello from mixin!')
    }
  }
}

// 定义一个使用混入对象的组件
var Component = Vue.extend({
  mixins: [myMixin]
})
  • 选项合并

当组件和混入对象含有同名选项时,这些选项将以恰当的方式进行“合并”。

比如,数据对象在内部会进行递归合并,并在发生冲突时以组件数据优先

var mixin = {
  data: function () {
    return {
      message: 'hello',
      foo: 'abc'
    }
  }
}

new Vue({
  mixins: [mixin],
  data: function () {
    return {
      message: 'goodbye',
      bar: 'def'
    }
  },
  created: function () {
    console.log(this.$data)
    // => { message: "goodbye", foo: "abc", bar: "def" }
  }
})

同名钩子函数将合并为一个数组,因此都将被调用。另外,混入对象的钩子将在组件自身钩子之前调用

var mixin = {
  created: function () {
    console.log('混入对象的钩子被调用')
  }
}

new Vue({
  mixins: [mixin],
  created: function () {
    console.log('组件钩子被调用')
  }
})
// => "混入对象的钩子被调用"
// => "组件钩子被调用"

值为对象的选项,例如 methods、components 和 directives,将被合并为同一个对象。两个对象键名冲突时,取组件对象的键值对。

var mixin = {
  methods: {
    foo: function () {
      console.log('foo')
    },
    conflicting: function () {
      console.log('from mixin')
    }
  }
}

var vm = new Vue({
  mixins: [mixin],
  methods: {
    bar: function () {
      console.log('bar')
    },
    conflicting: function () {
      console.log('from self')
    }
  }
})

vm.foo() // => "foo"
vm.bar() // => "bar"
vm.conflicting() // => "from self"

全局混入不建议使用

插件

插件通常用来为 Vue 添加全局功能. (插件的功能范围没有严格的限制)

通过全局方法 Vue.use() 使用插件. (需要在调用 new Vue() 启动应用之前完成)

// 调用 `MyPlugin.install(Vue)`
Vue.use(MyPlugin)

new Vue({
  // ...组件选项
})

本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据。

  1. 定义插件:
对象.install = function (Vue, options) {
    // 1. 添加全局过滤器
    Vue.filter(....)

    // 2. 添加全局指令
    Vue.directive(....)

    // 3. 配置全局混入(合)
    Vue.mixin(....)

    // 4. 添加实例方法
    Vue.prototype.$myMethod = function () {...}
    Vue.prototype.$myProperty = xxxx
}
  1. 使用插件案例:

plugin.js

export default {
    install(Vue, x, y, z) {
        console.log(x, y, z)
        //全局过滤器
        Vue.filter('mySlice', function (value) {
            return value.slice(0, 4)
        })

        //定义全局指令
        Vue.directive('fbind', {
            //指令与元素成功绑定时(一上来)
            bind(element, binding) {
                element.value = binding.value
            },
            //指令所在元素被插入页面时
            inserted(element, binding) {
                element.focus()
            },
            //指令所在的模板被重新解析时
            update(element, binding) {
                element.value = binding.value
            }
        })

        //定义混入
        Vue.mixin({
            data() {
                return {
                    x: 100,
                    y: 200
                }
            },
        })

        //给Vue原型上添加一个方法(vm和vc就都能用了)
        Vue.prototype.hello = () => { alert('你好啊aaaa') }
    }
}

main.js

// 引入插件
import plugin from './plugin'

// 使用插件
Vue.use(plugin)

然后就可以在别的组件使用插件里的功能了。

二、选取local storage:

Vue.ls.get(USER_INFO)["username"]
//需安装Vue-ls插件
npm install vue-ls —save //npm
yarn add vue-ls //yarn

三、缩写符号

// 1. @xxx:v-on
<div :class 等效于 <div v-bind:class
// 2. :xxx:v-bind
<div @click 等效于 <div v-on:click
// 3. #xxx:v-slot
<template #footer 等效于 <template v-slot:footer

四、v-model(双向绑定)其实等于两个结合:

  1. 约等于(1/2):v-bind;数据层的改变引起视图层的变化。

  2. 约等于(2/2):监听事件;视图层的变化引起数据层的变化。

v-model 不会触发生命周期,所以组件初始化方法写在比如 mounted() 里面会失效。使用 v-if 会重新渲染整个 dom 树,所以会走生命周期,dialog 标签加上 v-if=“visibleFlag” 属性就行了。

五、MVVM

MVVM

六、v-if vs v-show

  1. v-if 能在 template 上使用, show 不行
  2. if 能搭配 keep-alive 使用, show 不行
  3. if 存在就近复用的问题, 要加 key, show 不用
  4. if 会在一瞬间出现, show 会有弹出动画

七、v-slot 指令

v-slot 取代了 slot 和 slot-scope 的应用, 不过 v-slot 只能用在 template 标签上

  1. slot: 父组件使用 <template> 在子组件 <slot></slot> 处插入内容
  2. 编译作用域 (父组件 在子组件 <slot></slot> 处插入 data)
  3. 后备内容 (子组件 <slot> </slot> 设置默认值)
  4. 具名插槽 (子组件 多个 <slot ></slot> <slot name="first"></slot> 对应被插入内容, 父组件通过 v-slot:name 的方式插入内容, v-slot:name 可以简写为 #name)
    1. <template v-slot:one> 这是插入到one插槽的内容 </template>
  5. 作用域插槽 (父组件 在子组件 <slot> </slot> 处使用子组件 data)
  • slot 和 slot-scope

Parent

<template>
  <div id="app">
    <child-component :msg="gg">
      <ul slot-scope="props123">
        <li>foo: {{props123.foo}}</li>
        <li>msg: {{props123.msg321}}</li>
      </ul>
    </child-component>
  </div>
</template>

<script>
import ChildComponent from "./components/ChildComponent";

export default {
  name: "App",
  components: {
    ChildComponent
  },
  data() {
    return {
      gg: "hello???"
    }
  }
};
</script>
<style>
</style>

Children

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <slot :foo="foo1" :msg321="msg"></slot>
  </div>
</template>

<script>
export default {
  name: "ChildComponent",
  props: {
    msg: String
  },
  data() {
    return {
      foo1: "bar123"
    };
  }
};
</script>

<style scoped>
<!-- 对于所有的 Vue 组件,只要设置了 `<style scoped></style>`, Vue就会给该组件生成一个唯一data值 -->
</style>
  • scoped样式
    • 作用:让样式在局部生效,防止冲突
    • 写法:<style scoped>

对于所有的 Vue 组件,只要设置了 <style scoped></style>, Vue就会给该组件生成一个唯一data值

八、watch vs computed

both base Reactive Effect class, throught getter and setter update view.

ref 有个 value, 因为 ref + value 构成对象传给 reactive 了

九、脚手架

1. init

npm init/create vue@3

2. 大版本.^次要版本.~小版本

波浪号 (tilde): 比如~1.2.2,表示安装 1.2.x 的最新版本. (安装时不改变大版本号和次要版本号)
插入号 (caret): 比如^1.2.2,表示安装 1.x.x 的最新版本. (安装时不改变大版本号)

注意,如果大版本号为0,则插入号的行为与波浪号相同,这是因为此时处于开发阶段,即使是次要版本号变动,也可能带来程序的不兼容

latest:安装最新版本

3. 正则

let reg = /1234/g
​i: ignoreCase, 匹配忽视大小写
m: multiline , 多行匹配
g: global , 全局匹配

4. 初始化 vue2 项目 (使用 vite -> vue2.7.x: 支持组合式api)

  • shell
npm init vue@2
npm i element-ui -S
npm i axios --save
npm i vue-axios --save
  • 目录格式
├── node_modules 
├── public
│   ├── favicon.ico: 页签图标
│   └── index.html: 主页面
├── src
│   ├── assets: 存放静态资源
│   │   └── logo.png
│   │── component: 存放组件
│   │   └── HelloWorld.vue
│   │── App.vue: 汇总所有组件
│   │── main.js: 入口文件
├── .gitignore: git版本管制忽略的配置
├── babel.config.js: babel的配置文件
├── package.json: 应用包配置文件 
├── README.md: 应用描述文件
├── package-lock.json:包版本控制文件

十、模拟后端服务器提供json数据

npx json-server --watch data/db.json --port 8000

db.json

{
	"url": [
		{ "...": "...." },
		{ "...": "...." }
	]
}

Endpint

/url  GET/GET_PATH{id}/POST/DELETE
Logo

前往低代码交流专区

更多推荐