发布时间:2023-04-16 13:30
如何使用vue完成项目的开发
深度理解特性背后的原理
DOCTYPE html>
<html lang=\"en\">
<head>
<meta charset=\"UTF-8\">
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">
<title>hello worldtitle>
<script src=\"https://unpkg.com/vue@next\">script>
head>
<body>
<div id=\"root\">div>
body>
<script>
// Vue.createApp({ // 创建vue实例
// template: \'hello world\' // 在root元素里面用此模板展示内容
// }).mount(\'#root\'); // 在id为root的元素上使用vue
/**
* 1.找到root节点,往上面挂一个元素
* 2.元素的内容就是template里面的内容
* 3.用{{}}}:表示这是js表达式,是一个变量
*
* {{content}}
*
*/
Vue.createApp({
data() { // 定义数据
return {
content: 1
}
},
// 当页面加载完成,就会执行mounted函数,自动执行函数
mounted() { // 自动执行
// console.log(\'mounted\')
setInterval(() => {
this.content += 1
}, 1000)
},
template: \'{{content}}\' // 渲染内容
}).mount(\'#root\'); // 渲染节点
script>
html>
此后的代码省略了html的head、body部分,只写js部分代码
// 反转字符串
// Vue.createApp({
// data() {
// return {
// content: \'hello world\'
// }
// },
// methods: { // 定义函数
// handleBtnClick() {
// // 将content内容反转
// this.content = this.content.split(\'\').reverse().join(\'\')
// }
// },
// template: `
//
// {{ content }}
//
//
//
// `
// }).mount(\'#root\');
// 显示/隐藏
Vue.createApp({
data() {
return {show: true}
},
methods: {
handleBtnClick() {
// 显示/隐藏
this.show = !this.show
}
},
template: `
hello world
`
}).mount(\'#root\');
// 循环list数据
// Vue.createApp({
// data() {
// return {
// list: [\'hello\', \'world\', \'dell\', \'lee\']
// }
// },
// template: `
//
//
// {{item}} {{index}}
//
// `
// }).mount(\'#root\');
// 增加
Vue.createApp({
data() {
return {
inputValue: \'\',
list: []
}
},
methods: {
handleAddItem() {
this.list.push(this.inputValue)
this.inputValue = \'\' // 增加完之后,input框清空
}
},
template: `
- {{ item }}
`
}).mount(\'#root\');
const app = Vue.createApp({ // 创建一个vue实例app
data() {
return {
inputValue: \'\',
list: []
}
},
methods: {
handleAddItem() {
this.list.push(this.inputValue)
this.inputValue = \'\'
}
},
template: `
`
})
// 在app上注册组件:组件是页面的一部分
app.component(\'todo-item\', {
props: [\'content\', \'index\'], // 接收外部传递的内容
template: \'{{index}} -- {{content}} \'
})
// 在app上挂载实例
app.mount(\'#root\')
// createApp:创建一个Vue应用,存储到app变量当中
// 传入的参数:这个应用最外层的组件该如何展示
// mvvm设计模式: m->model 数据,v->view 视图,vm->viewModel 视图数据连接层
const app = Vue.createApp({
// 以下内容是app的参数
// model
data() {
return {
message: \'hello world\'
}
},
// view
template: \"{{message}}\"
})
// vm:Vue的根组件,viewModel
const vm = app.mount(\'#root\')
// 可以通过vm操作数据
// vm.$data.message = \'bye\'
/**
* 生命周期函数:在某一时刻会自动执行的函数 (钩子=生命周期函数)
* 关键点:某一时刻
* 自动执行:eg.mounted()
*/
const app = Vue.createApp({
data() {
return {
message: \'hello world\'
}
},
// 在实例生成之前自动执行的函数
beforeCreate() {
console.log(\'beforeCreate\')
},
// 在实例生成之后自动执行的函数
created() {
console.log(\'created\')
},
// 在组件内容被渲染到页面之前自动执行的函数
beforeMount() {
console.log(document.getElementById(\'root\').innerHTML, \'beforeMount\') // beforeMount
},
// 在组件内容被渲染到页面之后自动执行的函数
mounted() {
console.log(document.getElementById(\'root\').innerHTML, \'mounted\') // hello world mounted
},
// 当数据发生变化时,会自动执行的函数
beforeUpdate() {
console.log(document.getElementById(\'root\').innerHTML, \'beforeUpdate\') // hello world beforeUpdate
},
// 当数据发生变化,页面重新渲染后,会自动执行的函数
updated() {
console.log(document.getElementById(\'root\').innerHTML, \'updated\') // bye beforeUpdate
},
// 当vue应用场景失效时,会自动执行的函数
beforeUnmount() {
console.log(document.getElementById(\'root\').innerHTML, \'beforeUnmount\') // hello world beforeUnmount
},
// 当vue应用场景失效时,且 dom 完全销毁后,会自动执行的函数
unmounted() {
console.log(document.getElementById(\'root\').innerHTML, \'unmounted\') // unmounted
},
// 如果没有template属性,内容写在了body下的div root下,生命周期函数也是如此
template: \"{{message}}\" // 先将模板变成函数,再渲染
})
const vm = app.mount(\'#root\')
const app = Vue.createApp({
data() {
return {
message: \'hello world\',
disabled: false,
show: true,
event: \'click\' // 动态参数
}
},
methods: {
handleClick() {
alert(\'click\')
},
// pd(e) {
// e.preventDefault() // 阻止默认行为
// }
},
template: `
{{ message }}
hello world
{{ message }}
{{ message }}
`
})
app.mount(\'#root\')
/**
* data methods computed watcher
* computed和methods实现相同的功能时,选computed,因为有缓存
* computed和watcher实现相同的功能时,选computed,因为更简洁
*/
const app = Vue.createApp({
data() {
return {
message: \'hello world\',
count: 2,
price: 5,
newTotal: 10,
}
},
methods: {
// 方法中this指向vue实例
// 不要使用箭头函数,因为箭头函数的this指向其外层的this
handleClick() {
console.log(\'click\', this)
},
formatString(string) {
return string.toUpperCase()
},
// 只要页面重新渲染,才会重新计算
getTotal() {
return Date.now()
// return this.count * this.price
}
}, computed: {
total() {
// 当计算属性依赖的内容发生变更时,才会重新执行计算
return Date.now()
// return this.count * this.price
}
},
// 当遇到异步情况的时候,可以使用watch
// watch可以监听变量的改变,进行异步操作
watch: {
// price发生变化时函数会执行
// price() {
// setTimeout(() => {
// console.log(\'price change\')
// }, 1000)
// },
// 也可以实现计算属性的功能,实际上watch就是计算属性的底层实现
price(cur, pre) {
console.log(cur, pre)
this.newTotal = cur * this.count
}
},
template: `
{{ formatString(message) }}
{{ message }} total:{{ total }} getTotal():{{ getTotal() }}
newTotal:{{ newTotal }}
`
})
const vm = app.mount(\'#root\')
const app = Vue.createApp({
data() {
return {
classString: \'red\',
classObject: {red: true, green: true}, // 变量名:展示的样式值,变量值:是否展示
classArray: [\'red\', \'green\', {brown: true}],
styleString: \'color: yellow\', // 不建议使用
styleObject: { // 建议使用对象的形式来写
color: \'orange\',
background: \'skyblue\'
}
}
},
template: `
hello world
hello world
hello world
hello world
hello world
hello world
hello world
`
})
app.component(\'demo\', {
template: `
one
two
`
})
const vm = app.mount(\'#root\')
const app = Vue.createApp({
data() {
return {
show: true,
conditionOne: false,
conditionTwo: true,
}
},
template: `
hello world
hello world
if
else
else
`
})
const vm = app.mount(\'#root\')
const app = Vue.createApp({
data() {
return {
listArray: [\'hello\', \'world\'],
listObject: {
name: \'mys\',
job: \'student\'
},
}
},
methods: {
handleAddBtnClick() {
// 1.使用数组的变更函数 push pop shift unshift splice sort reverse
// this.listArray.push(\'hello\')
// 2. 直接替换数组
// this.listArray = [\'bye\', \'world\'].filter(item => item === \'bye\')
// 3.直接更新数组的内容
// this.listArray[1] = \'hhhh\'
// 直接添加对象的内容,也可以自动的展示出来
this.listObject.age = \'18\'
this.listObject.sex = \'female\'
}
},
template: `
{{ item }} -- {{ index }}
{{ key }} -- {{ value }} -- {{ index }}
{{ item }}
`
})
const vm = app.mount(\'#root\')
template: `
{{ counter }}
div自身
鼠标修饰符
精确饰符
`
})
const app = Vue.createApp({
data() {
return {
message: \'hello\',
checkbox: [],
radio: \'\',
select: \'\',
options: [{
text: \'A\', value: \'A\',
}, {
text: \'B\', value: \'B\',
}, {
text: \'C\', value: {value: \'C\'},
}],
checkboxValue: \'hello\',
lazy: \'lazy\',
number: 12,
}
},
template: `
{{ message }}
`
})
const vm = app.mount(\'#root\')
/**
* 定义一个局部组件,在app中不能直接使用,
* 需要写上:components: {\'mys\': counter_part}引入局部组件
* 定义局部组件,通常将首字符大写
*/
const CounterPart = {
data() {
return {
count: 1
}
},
template: `{{count}}`
}
const HelloWorld = {
template: `hello world`
}
const app = Vue.createApp({
/**
* 创建一个vue实例,渲染根组件
* 组件:对页面的展示
* 组件可以被复用,且其数据相互独立
* 组件在app中定义了,即使没使用,也会挂在app上
* 在app中定义的组件是全局组件,在处处都能使用,性能不高,使用简单,名字通常用小写字母,中间用-隔开
* 局部组件需要定义并注册后才能使用,性能高,但是使用麻烦,名字通常大写字母开头,驼峰式命名
* 局部组件使用时,需要名字和组件间映射对象,如果不写映射,按照上面的命名方式,vue底层会自动映射
*/
// 在vue实例中使用components引入局部组件
components: {
// \'CounterPart\': CounterPart,
// CounterPart 命名和组件名相同可以简写
// \'hello-world\': HelloWorld
// vue会自动进行映射,将驼峰命名格式映射为:counter-part短横线分割的格式
CounterPart, HelloWorld
},
template: `
`
})
// 定义子组件 (全局组件)
app.component(\'hello\', {
template: `hello`
})
app.component(\'world\', {
template: `world`
})
app.component(\'counter-parent\', {
template: ` `
})
app.component(\'counter\', {
data() {
return {
count: 1
}
},
template: `{{count}}`
})
const vm = app.mount(\'#root\')
const app = Vue.createApp({
data() {
return {
num: 123,
// num: () => alert(\'num\')
}
},
template: `
`
})
/**
* 3.父组件->子组件:接收数据、类型校验
* type:String,Boolean,Array,Object,Function,Symbol
* required: 必选
* default:默认值
*/
// 创建一个全局组件
app.component(\'test\', {
// props: [\'content\'], 直接接收父组件传递来的数据
// 如果需要对父组件传递来的数据做验证
props: {
// 如果传递来的数据不是String类型的,控制台会出现警告提示:type check failed for prop \"content\"
// content: String,
// content:Function,
content: {
type: Number,
// default: 123, // 默认值
// required: true, // 如果没写default,不传参会有警告
// 深度校验
validator: function (value) { // value父组件传递来的值
return value < 1000
},
// default也可以是一个函数
default: function () {
return 456
}
},
},
methods: {
handleClick() {
alert(\'handleClick\')
this.content() // 调用父组件传递来的函数
}
},
template: `
{{ content }}`
})
const vm = app.mount(\'#root\')
const app = Vue.createApp({
data() {
return {
params: {
content: 123,
a: 1,
b: 2,
},
dataAbc: 123,
num: 1,
}
},
template: `
`
})
app.component(\'test\', {
props: [\'content\', \'a\', \'b\', \'dataAbc\'],
template: `
{{ content }} -- {{ a }} -- {{ b }} -- {{ dataAbc }}
`
})
/**
* 3.单向数据流
* 父组件可以向子组件传递数据,但数据是只读的,子组件不能修改
* 避免组件之间的数据耦合
*/
app.component(\'counter\', {
props: [\'count\'],
data() {
return {
myCount: this.count
}
},
template: `
{{ myCount }}
`
})
const vm = app.mount(\'#roo
/**
* Non-props属性:
* 父组件给子组件传递数据时,子组件不通过props接收,
* 此时底层会将父组件传递过来的属性放在最外层div属性上,
* 如果在子组件上写:inheritAttrs: false,就不会放在最外层div上
* 使用:通常用于 style class属性
*/
const app = Vue.createApp({
template: `
`
})
app.component(\'counter\', {
// inheritAttrs: false,
mounted() {
console.log(this.$attrs.msg) // hello
},
template: `
Counter
Counter
Counter
`,
})
const vm = app.mount(\'#root\')
/**
* 父子组件通信的流程:
* 1.子组件接收父组件传递来的count
* 2.点击时触发了事件:handleClick,该事件向父组件传递:add事件,也能传递参数
* 3.父组件监听到add事件,通过handleAdd进行处理,修改count
* 4.最后修改了的count传递给了子组件
*/
const app = Vue.createApp({
data() {
return{
count: 1
}
},
methods: {
handleAdd(param) {
this.count += param
}
},
template: `
`
})
app.component(\'counter\', {
props: [\'count\'],
// emits:表示触发了哪些事件
emits: [\'add\'],
// embeds: {
// // 可以对参数进行校验
// add: (count) => {
// if (count > 0) {
// return true
// }
// return false
// }
// },
methods: {
handleClick() {
this.$emit(\'add\', 2)
// 事件处理的逻辑也可以直接在子组件内完成,再传递给父组件
// this.$emit(\'add\', this.count + 3)
}
},
template: `
{{ count }}
`,
})
const vm = app.mount(\'#root\')
使用v-model简化
/**
* 使用v-model简化
* 1.父组件中定义一个数据count,通过v-model传递给子组件
* 2.子组件使用modelValue接收参数,注意:接收参数名只能是:modelValue
* 3.点击触发事件handleClick,该事件向父组件传递的事件名只能是:update:modelValue
* 传递的参数modelValue会替换父组件的count
*/
const app = Vue.createApp({
data() {
return {
count: 1
}
},
template: `
`
})
app.component(\'counter\', {
props: [\'modelValue\'],
methods: {
handleClick() {
this.$emit(\'update:modelValue\', this.modelValue + 3)
}
},
template: `
{{ modelValue }}
`,
})
const vm = app.mount(\'#root\')
**1、父子组件间的通信 v-model 传递多个参数 **
/**
* 父子组件间的通信 v-model
* 传递多个参数
*/
const app = Vue.createApp({
data() {
return {
count: 1,
count1: 1,
}
},
template: `
`
})
app.component(\'counter\', {
props: [\'count\', \'count1\'],
methods: {
handleClick() {
this.$emit(\'update:count\', this.count + 3)
},
handleClick1() {
this.$emit(\'update:count1\', this.count1 + 3)
}
},
template: `
{{ count }}
{{ count1 }}
`,
})
const vm = app.mount(\'#root\')
2、修饰符的使用
/**
* 父子组件间的通信 v-model
* 修饰符的使用:
* .uppercase
*
* 1.父组件中:v-model.uppercase
* 2.父组件会传递一个modelModifiers参数接收修饰符,注意:modelModifiers名字是固定的
* 3.在点击事件handleClick,获取修饰符的值,并调用相应的函数进行处理
* 4.向父组件传递事件:update:modelValue和新的参数newValue
* 5.父组件获取到事件后,对count变量的值进行修改
*/
const app = Vue.createApp({
data() {
return {
count: \'a\',
}
},
template: `
`
})
app.component(\'counter\', {
props: {
\'modelValue\': String,
// 传递过来的修饰符
\'modelModifiers\': {
// 如果不传修饰符,默认空对象;如果传了修饰符会放到对象中
default: () => ({})
}
},
// mounted() {
// console.log(this.modelModifiers)
// },
methods: {
handleClick() {
let newValue = this.modelValue + \'b\'
if (this.modelModifiers.uppercase) {
newValue = newValue.toUpperCase()
}
this.$emit(\'update:modelValue\', newValue)
},
},
template: `
{{ modelValue }}
`,
})
const vm = app.mount(\'#root\')
/**
* slot 插槽
* slot不能绑定事件,可以在用一个span标签包裹slot,用于绑定事件
* slot中可以传递:标签、字符串、子组件
*
* slot中使用的数据作用域问题:
* 父模板里调用的数据属性,都用的是父模板里的数据
* 子模板里调用的数据属性,都用的是子模板里的数据
*
* slot的简写:v-slot:header => #header
*/
const app = Vue.createApp({
data() {
return {
text: \'提交\',
}
},
template: `
{{ text }}
header
footer
`
})
app.component(\'myform\', {
methods: {
handleClick() {
alert(\'click\')
}
},
template: `
default value
`,
})
app.component(\'layout\', {
template: `
content
`
})
const vm = app.mount(\'#root\')
/**
* 作用域插槽
* 1.父组件调用list的子组件,即传递给子组件的slot div
* 2.子组件调用slot,通过div的形式循环显示内容
* 3.调用slot时,将item数据通过slotProps数据对象传递给slot
*
* 当子组件渲染的内容由父组件决定时
* 通过作用域插槽,能够让父组件调用子组件的数据
*/
const app = Vue.createApp({
template: `
{{ item }}
`
})
app.component(\'list\', {
data() {
return {list: [1, 2, 3]}
},
template: `
`
})
const vm = app.mount(\'#root\')
/**
* 需求:通过一个变量控制input-item、common-item的展示与隐藏
*
* 动态组件:根据数据的变化,结合component标签,来随时动态切换组件的显示
* 异步组件:异步执行某些组件的逻辑
*/
const app = Vue.createApp({
data() {
return {currentItem: \'input-item\'}
},
methods: {
handleClick() {
this.currentItem = this.currentItem === \'input-item\' ? \'common-item\' : \'input-item\'
}
},
template: `
`
})
app.component(\'input-item\', {
template: `
`
})
app.component(\'common-item\', {
template: `
hello world
`
})
// 同步
app.component(\'sync-common-item\', {
template: `
hello world
`
})
// 异步
app.component(\'async-common-item\', Vue.defineAsyncComponent(() => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
template: `this is an async component`
})
}, 4000)
})
}))
const vm = app.mount(\'#root\')
/**
* v-once:让某个元素标签只渲染一次
* ref:获取dom节点 / 组件的一个用法 (直接操作dom是要慎重)
* provide / inject 多层组件传值,使用provide提供数据,inject注入数据
*/
const app = Vue.createApp({
data() {
return {count: 1}
},
// provide: {
// count: 2,
// },
// 如果是传递data中的数据,要使用函数形式
// 此处传递的数据是一次性的,需要类似双向绑定的效果需要响应的知识,后面会讲
provide() {
return {
count: this.count
}
},
mounted() {
// 获取dom最好在mounted中,因为此时页面已经加载完成。但是最好不要直接操作dom
// console.log(this.$refs.count.innerHTML = \'hello\')
// console.log(this.$refs.common.syaHello())
},
template: `
{{ count }}
`
})
app.component(\'common-item\', {
methods: {
syaHello() {
alert(\'hello\')
}
},
template: `hello world`
})
app.component(\'child\', {
props: [\'count\'],
// template: ``
template: ` `
})
app.component(\'child-child\', {
// props: [\'count\'],
inject: [\'count\'],
template: `{{ count }}`
})
const vm = app.mount(\'#root\')
未完…