初学Vue, 关于data内的数据not defined的问题
初学Vue, 关于data内的数据not defined的问题代码如下:<!--view层 模板--><div id="app" v-cloak><p>{{msg}}</p><div>{{info.name}}</div><div>{{info.grade.level}}</div><a :hre
TL’DR: 原因主要是重复定义了两个data导致的
other:父子组件通信、读取local storage、缩写符号、双向绑定、MVVM
一、问题截图
二、代码
<!--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
@
作为v-on
指令的缩写, 表示监听事件, 用于父子组件之间方法的传递, 为组件绑定事件. 子组件不需要接收, 在子组件的$listeners
上可以找到, 可以使用this.$emit('xxx')
手动触发
另外,"@/api/api"
中@
等价于/src 目录的相对路径:
作为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. 以下是 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. 以下是 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,第二个以后的参数是插件使用者传递的数据。
- 定义插件:
对象.install = function (Vue, options) {
// 1. 添加全局过滤器
Vue.filter(....)
// 2. 添加全局指令
Vue.directive(....)
// 3. 配置全局混入(合)
Vue.mixin(....)
// 4. 添加实例方法
Vue.prototype.$myMethod = function () {...}
Vue.prototype.$myProperty = xxxx
}
- 使用插件案例:
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/2):v-bind;数据层的改变引起视图层的变化。
-
约等于(2/2):监听事件;视图层的变化引起数据层的变化。
v-model 不会触发生命周期,所以组件初始化方法写在比如 mounted() 里面会失效。使用 v-if 会重新渲染整个 dom 树,所以会走生命周期,dialog 标签加上 v-if=“visibleFlag” 属性就行了。
五、MVVM
六、v-if vs v-show
- v-if 能在 template 上使用, show 不行
- if 能搭配 keep-alive 使用, show 不行
- if 存在就近复用的问题, 要加 key, show 不用
- if 会在一瞬间出现, show 会有弹出动画
七、v-slot 指令
v-slot 取代了 slot 和 slot-scope 的应用, 不过 v-slot 只能用在 template 标签上
- slot: 父组件使用
<template>
在子组件<slot></slot>
处插入内容 - 编译作用域 (父组件 在子组件
<slot></slot>
处插入data
) - 后备内容 (子组件
<slot> </slot>
设置默认值) - 具名插槽 (子组件 多个
<slot ></slot>
<slot name="first"></slot>
对应被插入内容, 父组件通过v-slot:name
的方式插入内容,v-slot:name
可以简写为#name
)<template v-slot:one> 这是插入到one插槽的内容 </template>
- 作用域插槽 (父组件 在子组件
<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
更多推荐
所有评论(0)