项目构建
官网
配置环境
终端
Linux
和Mac
上可以用自带的终端。
Windows
上推荐用powershell
或者cmd
。Git Bash
有些指令不兼容。
安装Nodejs
安装地址
安装@vue/cli
打开Git Bash
,执行:
npm i -g @vue/cli
如果执行后面的操作有bug,可能是最新版有问题,可以尝试安装早期版本,比如:npm i -g @vue/cli@4
启动vue
自带的图形化项目管理界面
vue ui
常见问题1:Windows
上运行vue
,提示无法加载文件,表示用户权限不足。
解决方案:用管理员身份打开终端,输入set-ExecutionPolicy RemoteSigned
,然后输入y
使用
Vue.createApp().mount('')//创建应用,将配置对象传入,mount进行挂载,和dom元素关联
CDN
你可以借助 script 标签直接通过 CDN 来使用 Vue:
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
使用全局构建版本
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<div id="app">{{ message }}</div>
<script>
const { createApp } = Vue
createApp({
data() {
return {
message: 'Hello Vue!'
}
}
}).mount('#app')
</script>
Vite
Vite 是一个轻量级的、速度极快的构建工具,对 Vue SFC 提供第一优先级支持。作者是尤雨溪,同时也是 Vue 的作者!
要使用 Vite 来创建一个 Vue 项目,非常简单:
npm init vue@latest
npm init vue@latest <project-name> -- -- template vue
这个命令会安装和执行 create-vue,它是 Vue 提供的官方脚手架工具。跟随命令行的提示继续操作即可。
- 要学习更多关于 Vite 的知识,请查看 Vite 官方文档。
- 若要了解如何为一个 Vite 项目配置 Vue 相关的特殊行为,比如向 Vue 编译器传递相关选项,请查看 @vitejs/plugin-vue 的文档。
上面提到的两种在线演练场也支持将文件作为一个 Vite 项目下载。
vue cli
vue create projectname
基本概念
script部分
export default
对象的属性:
name
:组件的名称components
:存储<template>
中用到的所有组件props
:存储父组件传递给子组件的数据watch()
:当某个数据发生变化时触发computed
:动态计算某个数据setup(props, context)
:初始化变量、函数ref
定义变量,可以用.value
属性重新赋值reactive
定义对象,不可重新赋值props
存储父组件传递过来的数据context.emit()
:触发父组件绑定的函数
template部分
<slot></slot>
:存放父组件传过来的children
。v-on:click
或@click
属性:绑定事件v-if
、v-else
、v-else-if
属性:判断v-for
属性:循环,:key
循环的每个元素需要有唯一的key
v-bind
:或:
:绑定属性
style部分
<style>标签添加scope属性后,不同组件间的css不会相互影响。
第三方组件
view-router
包:实现路由功能。vuex
:存储全局状态,全局唯一。state
: 存储所有数据,可以用modules
属性划分成若干模块getters
:根据state
中的值计算新的值mutations
:所有对state
的修改操作都需要定义在这里,不支持异步,可以通过$store.commit()
触发actions
:定义对state
的复杂修改操作,支持异步,可以通过$store.dispatch()
触发。注意不能直接修改state
,只能通过mutations
修改state
。modules
:定义state
的子模块
模板语法
内容渲染指令
v-text
<p v-text='msg1'></>
将数据源中的数据直接渲染到成为标签的内容- 缺点:v-text会直接覆盖掉标签内本来的值
{{}}
{{}} :插值表达式
插值表达式可以添加简单的JS表达式(加减乘除,三元表达式等)但是不能使用复杂的JS语句
姓名:{{msg1}}
最基本的数据绑定形式是文本插值,它使用的是“Mustache”语法 (即双大括号):
<span>Message: {{ msg }}</span>
双大括号标签会被替换为相应组件实例中 msg
属性的值。同时每次 msg
属性更改时它也会同步更新。
v-html
v-html:可以添加标签(html元素)
双大括号会将数据解释为纯文本,而不是 HTML。若想插入 HTML,你需要使用 v-html
指令:
template
<p>Using text interpolation: {{ rawHtml }}</p>
<p>Using v-html directive: <span v-html="rawHtml"></span></p>
这里我们遇到了一个新的概念。这里看到的 v-html
attribute 被称为一个指令。指令由 v-
作为前缀,表明它们是一些由 Vue 提供的特殊 attribute,你可能已经猜到了,它们将为渲染的 DOM 应用特殊的响应式行为。这里我们做的事情简单来说就是:在当前组件实例上,将此元素的 innerHTML 与 rawHtml
属性保持同步。
span
的内容将会被替换为 rawHtml
属性的值,插值为纯 HTML——数据绑定将会被忽略。注意,你不能使用 v-html
来拼接组合模板,因为 Vue 不是一个基于字符串的模板引擎。在使用 Vue 时,应当使用组件作为 UI 重用和组合的基本单元。
安全警告
在网站上动态渲染任意 HTML 是非常危险的,因为这非常容易造成 [XSS 漏洞](https://en.wikipedia.org/wiki/Cross-site_scripting)。请仅在内容安全可信时再使用 `v-html`,并且**永远不要**使用用户提供的 HTML 内容。
属性绑定指令
绑定 v-bind
双大括号不能在 HTML attributes 中使用。想要响应式地绑定一个 attribute,应该使用 v-bind
指令:
<div v-bind:id="dynamicId"></div>
v-bind
指令指示 Vue 将元素的 id
attribute 与组件的 dynamicId
属性保持一致。如果绑定的值是 null
或者 undefined
,那么该 attribute 将会从渲染的元素上移除。
简写为“:”
因为 v-bind
非常常用,我们提供了特定的简写语法:
<div :id="dynamicId"></div>
开头为 :
的 attribute 可能和一般的 HTML attribute 看起来不太一样,但它的确是合法的 attribute 名称字符,并且所有支持 Vue 的浏览器都能正确解析它。此外,他们不会出现在最终渲染的 DOM 中。简写语法是可选的,但相信在你了解了它更多的用处后,你应该会更喜欢它。
接下来的指引中,我们都将在示例中使用简写语法,因为这是在实际开发中更常见的用法。
布尔型
布尔型 attribute 依据 true / false 值来决定 attribute 是否应该存在于该元素上。disabled
就是最常见的例子之一。
v-bind
在这种场景下的行为略有不同:
<button :disabled="isButtonDisabled">Button</button>
当 isButtonDisabled
为真值或一个空字符串 (即 <button disabled="">
) 时,元素会包含这个 disabled
attribute。而当其为其他假值时 attribute 将被忽略。
动态绑定多个值
如果你有像这样的一个包含多个 attribute 的 JavaScript 对象:
data() {
return {
objectOfAttrs: {
id: 'container',
class: 'wrapper'
}
}
}
通过不带参数的 v-bind
,你可以将它们绑定到单个元素上:
template
<div v-bind="objectOfAttrs"></div>
事件绑定指令
v-on或@
<button @click="increment">{{ count }}</button>
v-on
- 原生DOM对象去掉on即可 – v-on:click – v-on:input – v-on:keyup
- 格式:v-on:事件名称=”事件处理函数的名称”
- 简写:@事件名称=”事件处理函数的名称”
- 绑定的事件可以用小括号进行传参,若无小括号则默认传触发的事件对象(MouseEvent{}),若在有参数的情况下,仍需获取事件对象,可使用
$event
符号获取事件对象。(@click=”add(n,$event)”)不过开发中不常用
事件修饰符(加在事件绑定后面)
@click.prenvent=”add”: 点击的同时,阻止默认行为(event.preventDefault() )
form表单提交会默认刷新网页,可以用@submit.prevent阻止
.stop:阻止事件冒泡
.capture: 以捕获模式触发当前的事件处理函数
.once: 绑定的事件只触发一次
.self: 只有在event.target是当前元素自身时触发事件处理函数
按键修饰符(仅用于键盘事件)
- @keyup.enter=””: 仅按下回车键时触发
- @keyup.esc=””: 仅按下esc时触发
双向绑定指令
- 即双向数据绑定指令:v-model
- 只有表单元素才能使用v-model,其他元素或无法呈现内容,或无法修改内容均无法使用该指令
- 表单元素均可使用(可理解为带value属性的标签可用),如:
- input输入框
- type=”radio”
- type=”checkbox”
- type=”xxxx”
- textarea文本域
- select下拉菜单
- input输入框
v-model指令的修饰符
- .number:自动将用户输入值转为数值类型
- .trim:自动过滤用户输入的首尾空白字符
- .lazy:在”change”时而非”input”时更新
- 中间变化的时候不会同步到data数据源中,只会在最后输入完成后改变
条件渲染指令
v-show
v-show:为元素添加或删除display:none样式来隐藏或显示元素(频繁切换时建议使用)
v-show
的用法与下面的v-if
类似,用上次的代码,直接将if
修改成show
即可。
v-if
- v-if和v-show
- v-if:动态创建和删除元素(页面创建时即为false只有特殊情况才会让其显示时用v-if)
- v-show:为元素添加或删除display:none样式来隐藏或显示元素(频繁切换时建议使用)
- 以上仅为面试时回答,现在电脑对此消耗不大,绝大多数情况无需考虑性能直接使用v-if
与v-if配合使用的v-else-if()和v-else
<div id="test">
<div v-if="score<60">不及格</div>
<div v-else-if="score<90">良好</div>
<div v-else>优秀</div>
</div>
v-for
data: {
list: [1, 2, 3, 4, 5]
}
<div v-for="item in userList" v-bind:key="item.id">
<ul>
<li v-for="(item,i) in list">索引:{{i}}---每一项:{{item}}</li>
</ul>
结果:
索引:0---每一项:1
索引:1---每一项:2
索引:2---每一项:3
索引:3---每一项:4
官方建议使用v-for时,后面一定要绑定一个:key=’item.id'(.vue文件中不绑key会直接报错)而且,尽量把id作为key的值
- 可提升性能,防止列表状态紊乱
- 官方对id的值类型有要求:字符串或数字
- key值不允许重复
- 使用索引号index没有用处,因为数组增删改查会改变索引号所对应的数据(如:在数组前面增删数据)
插槽<slot>
用于子元素占位,可在父元素中被替换
具名插槽
占位:
<slot name="slotname"></slot>
使用:
<template v-slot:slotname>替换的内容</template>
备用内容
在插槽没有提供内容是被渲染
作用域插槽
……
状态选项
data
用于声明组件初始响应式状态的函数。
- 详细信息
该函数应当返回一个普通 JavaScript 对象,Vue 会将它转换为响应式对象。实例创建后,可以通过
this.$data
访问该响应式对象。组件实例也代理了该数据对象上所有的属性,因此this.a
等价于this.$data.a
。所有会用到的顶层数据属性都应该提前在这个对象中声明。虽然理论上可以向
this.$data
添加新属性,但并不推荐这么做。如果一个属性的值在一开始还获取不到,应当先用undefined
或是null
值来占位,让 Vue 知道这个属性是存在的。以
_
或$
开头的属性将不会被组件实例代理,因为它们可能和 Vue 的内置属性、API 方法冲突。你必须以this.$data._property
的方式访问它们。不推荐返回一个可能改变自身状态的对象,如浏览器 API 原生对象或是带原型的类实例等。理想情况下,返回的对象应是一个纯粹代表组件状态的普通对象。
- 示例
export default { data() { return { a: 1 } }, created() { console.log(this.a) // 1 console.log(this.$data) // { a: 1 } } }
注意,如果你为
data
属性使用了一个箭头函数,则this
将不会指向该组件实例,不过你仍然可以通过该函数的第一个参数来访问实例:data: (vm) => ({ a: vm.myProp })
methods
用于声明要混入到组件实例中的方法。
- 详细信息
声明的方法可以直接通过组件实例访问,或者在模板语法表达式中使用。所有的方法都会将它们的
this
上下文自动绑定为组件实例,即使在传递时也如此。在声明方法时避免使用箭头函数,因为它们不能通过
this
访问组件实例。 - 示例
export default { data() { return { a: 1 } }, methods: { plus() { this.a++ } }, created() { this.plus() console.log(this.a) // => 2 } }
- 参考:Event 处理
watch
用于声明在数据更改时调用的侦听回调。
为了便于阅读,对类型进行了简化。
- 详细信息
watch
选项期望接受一个对象,其中键是需要侦听的响应式组件实例属性 (例如,通过data
或computed
声明的属性)——值是相应的回调函数。该回调函数接受被侦听源的新值和旧值。除了一个根级属性,键名也可以是一个简单的由点分隔的路径,例如
a.b.c
。注意,这种用法不支持复杂表达式——仅支持由点分隔的路径。如果你需要侦听复杂的数据源,可以使用命令式的$watch()
API。值也可以是一个方法名称的字符串 (通过
methods
声明),或包含额外选项的对象。当使用对象语法时,回调函数应被声明在handler
中。额外的选项包含:immediate
:在侦听器创建时立即触发回调。第一次调用时,旧值将为undefined
。deep
:如果源是对象或数组,则强制深度遍历源,以便在深度变更时触发回调。详见深层侦听器。flush
:调整回调的刷新时机。详见回调的触发时机及watchEffect()
。onTrack / onTrigger
:调试侦听器的依赖关系。详见侦听器调试。
声明侦听器回调时避免使用箭头函数,因为它们将无法通过
this
访问组件实例。 - 示例
export default { data() { return { a: 1, b: 2, c: { d: 4 }, e: 5, f: 6 } }, watch: { // 侦听根级属性 a(val, oldVal) { console.log(`new: ${val}, old: ${oldVal}`) }, // 字符串方法名称 b: 'someMethod', // 该回调将会在被侦听的对象的属性改变时调动,无论其被嵌套多深 c: { handler(val, oldVal) { console.log('c changed') }, deep: true }, // 侦听单个嵌套属性: 'c.d': function (val, oldVal) { // do something }, // 该回调将会在侦听开始之后立即调用 e: { handler(val, oldVal) { console.log('e changed') }, immediate: true }, // 你可以传入回调数组,它们将会被逐一调用 f: [ 'handle1', function handle2(val, oldVal) { console.log('handle2 triggered') }, { handler: function handle3(val, oldVal) { console.log('handle3 triggered') } /* ... */ } ] }, methods: { someMethod() { console.log('b changed') }, handle1() { console.log('handle 1 triggered') } }, created() { this.a = 3 // => new: 3, old: 1 } }
- 参考:侦听器
export default {
//组件的data选项是一个函数。Vue 会在创建新组件实例的过程中调用此函数。它应该返回一个对象,然后Vue 会通过响应性系统将其包裹起来,并以$data的形式存储在组件实例中。为方便起见,该对象的任何顶级property 也会直接通过组件实例暴露出来:
data() {
return {
count: 0
}
},
//要为组件添加方法,我们需要用到 methods 选项。其是一个包含所有方法的对象:
methods: {
increment() {
this.count++
}
}
}
和组件实例上的其他属性一样,方法也可以在模板上被访问。在模板中它们常常被用作事件监听器:
<button @click="increment">{{ count }}</button>
computed
接受一个 getter 函数,返回一个只读的响应式 ref 对象。该 ref 通过 .value
暴露 getter 函数的返回值。它也可以接受一个带有 get
和 set
函数的对象来创建一个可写的 ref 对象。
- 示例
data(){ message:"safasddf" }, computed:{//计算属性,只要依赖值不变,那么就不会重新计算(有缓存)---该示例的依赖值为message reverseMsg=()=>{ return this.message.split('').reverse().join('') } }
创建一个只读的计算属性 ref:
const count = ref(1) const plusOne = computed(() => count.value + 1) console.log(plusOne.value) // 2 plusOne.value++ // 错误
创建一个可写的计算属性 ref:(计算属性默认只有getter,在需要的时候可以提供一个setter)
const count = ref(1) const plusOne = computed({ get: () => count.value + 1, set: (val) => { count.value = val - 1 } }) plusOne.value = 1 console.log(count.value) // 0
调试:
const plusOne = computed(() => count.value + 1, { onTrack(e) { debugger }, onTrigger(e) { debugger } })
- 参考:
组件传值
父组件调用子组件(子组件为小模块)
父子组件访问方式
- 父组件访问子组件:$refs
- 子组件访问父组件:$parent (尽量少用,最好使用props传值)
- 子组件访问根组件:$root
父组件传子组件Props
子组件
props 需要使用 props
选项来定义:
export default {
props: ['foo'],
created() {
// props 会暴露到 `this` 上
console.log(this.foo)
}
}
除了使用字符串数组来声明 prop 外,还可以使用对象的形式:
export default {
props: {
title: String,
likes: Number
}
}
对于以对象形式声明中的每个属性,key 是 prop 的名称,而值则是该 prop 预期类型的构造函数。比如,如果要求一个 prop 的值是 number
类型,则可使用 Number
构造函数作为其声明的值。
父组件
静态值形式的 props:
<BlogPost title="My journey with Vue" />
使用 v-bind
或缩写 :
来进行动态绑定的 props:
<!-- 根据一个变量的值动态传入 -->
<BlogPost :title='title' />
<BlogPost :title="post.title" />
<!-- 根据一个更复杂表达式的值动态传入 -->
<BlogPost :title="post.title + ' by ' + post.author.name" />
子组件传父组件
简单版本
子组件写入props
父组件
<template>
<div>
<Child ref="myid"></Child>
</div>
</template>
<script>
export default{
data(){
return {};
},
methods:{
},
mounted(){
console.log(this.$refs.myid);
}
}
<script/>
自定义事件
子组件
通过$emit来触发事件
export default{
data(){
return msg:"hello"
},
methods:{
//在子组件中,通过$emit来触发事件
sendParent:function(){
//this.$emit('自定义事件的名称','发送的事件参数')
this.$emit('injectMsg',this.msg)
}
}
父组件
<template>
<div>
<Content @injectMsg="ChildMsg">
</div>
</template>
export default{
data(){
return {};
},
methods:{
//在子组件中,通过$emit来触发事件
getChildMsg:function(value){
//接收到的值就是子组件传来的值
console.log(value)
}
}
组件跨级通信
Provide/Inject
生命周期
- beforeCreate(在组件实例初始化完成之后立即调用。)
- created(在组件实例处理完所有与状态相关的选项后调用。)
- beforeMount(在组件被挂载之前调用。)
- mounted(在组件被挂载之后调用。)
- beforeUpdate(在组件即将因为一个响应式状态变更而更新其 DOM 树之前调用。)
- updated(在组件因为一个响应式状态变更而更新其 DOM 树之后调用。)
- beforeUnmount(在一个组件实例被卸载之前调用。)
- unmounted(在一个组件实例被卸载之后调用。)
- errorCaptured(在捕获了后代组件传递的错误时调用。)
- enderTracked (在一个响应式依赖被组件的渲染作用追踪后调用。)
- renderTriggered (在一个响应式依赖被组件触发了重新渲染之后调用。)
- activated(若组件实例是
<KeepAlive>
缓存树的一部分,当组件被插入到 DOM 中时调用。) - deactivated(若组件实例是
<KeepAlive>
缓存树的一部分,当组件从 DOM 中被移除时调用。) - serverPrefetch (当组件实例在服务器上被渲染之前要完成的异步函数。)
setup()
介绍
vue3->组合式API:将同一个逻辑关注点相关代码收集在一起
setup() 在组件被创建之前执行
注意:在setup
中你应该避免使用this
,因为它不会找到组件实例。setup
的调用发生在dataproperty
、computed property
或methods
被解析之前,所以它们无法在setup
中被获取。
setup
选项是一个接收props和context 的函数,我们将在之后进行讨论。此外。我们将setup
返回的所有内容都暴露给组件的其余部分(计算属性、方法、生命周期钩了等等)以及组件的模板。
export default {
setup(props, context) {
// 透传 Attributes(非响应式的对象,等价于 $attrs)
console.log(context.attrs)
// 插槽(非响应式的对象,等价于 $slots)
console.log(context.slots)
// 触发事件(函数,等价于 $emit)
console.log(context.emit)
// 暴露公共属性(函数)
console.log(context.expose)
return{ }
}
}
参数
props
setup
函数中的第一个参数是props
。正如在一个标准组件中所期望的那样, setup
函数中的props
是响应式的,当传入新的prop 时,它将被更新。
export default {
props:{
title: String
},
setup(props) {
console .log(props.title)
}
}
但是,因为props
是响应式的,你不能使用ES6解构,它会消除prop 的响应性。
如果需要解构prop,可以在setup
函数中使用toRefs
函数来完成此操作:
import { toRefs } from 'vue '
setup( props){
const { title } = toRefs (props)
console.log(title.value)
}
context
传递给setup
函数的第二个参数是context
。context
是一个普通JavaScript对象,暴露了其它可能在setup
中有用的值:
第二个参数 context
包含三个属性:attrs
、emit
和 slots
。它们分别相当于组件实例的 $attrs
、$emit
和 $slots
这几个属性。
export default {
setup(props,context){
// Attribute(非响应式对象,等同于 $attrs)
console.log( context.attrs)
//插槽(非响应式对象,等同于$slots)
console. log(context.slotE)
//触发事件(方法,等同于 $emit)
console.log( context.emit)
//暴露公共property(函数)
console.log ( context. expose))
}
响应式数据ref()
需要使用带ref
的响应式变量
ref()
接受一个内部值,返回一个响应式的、可更改的 ref 对象,此对象只有一个指向其内部值的属性 .value
。
ref
函数使得任何响应式变量在任何地方起作用
import { ref } from 'vue'
export default {
setup(props, context) {
const count = ref(0)
const increment = () => ++count.value
return {count,increment}
}
}
响应式对象reactive()
返回一个对象的响应式代理。
const obj = reactive(
{
name:"张三",
age:18
}
)
watch、watchEffect
就像我们在组件中使用watch
选项并在user
property上设置侦听器一样,我们也可以使用从Vue导入的 watch
函数执行相同的操作。它接受3个参数:
- 一个想要侦听的响应式引用或 getter函数
- 一个回调
- 可选的配置选项
watchEffect可以监听到属性
import { ref,reactive, watch,watchEffect } from 'vue'
export default{
data(){
return{}
},
setup(){
const counter = ref(0)
const user=reactive({
name:"张三",
age:18
})
//watch只能监听red响应式数据
watch(counter,(newValue,oldValue) =>{
console.log( "The new counter value is: ' + newValue)
console.log( "The pld counter value is: ' + oldValue)
})
//watchEffect注意不需要指定监听的属性,自动收集依赖
watchEffect() =>{
console.log(user.name)
})
return{counter,user}
},
}
computed
import { ref,reactive,computed } from 'vue'
export default{
data(){
return{}
},
setup(){
const msg=ref('helloworld')
const reverseMsg=computed(()=>{
return msg.value.split('').reverse().join(;)
})
return{msg}
},
}
生命周期钩子
可以通过生命周期钩子前面加上“on”来访问组件生命周期钩子
<script >
import {onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted,onErrorCaptured,onRenderTracked,onRenderTriggered,onActivated,onDeactivated,onServerPrefetch} from 'vue';
export default {
name: "index",
props: {},
components: {},
setup(props, context) {
//生命周期
onBeforeMount(()=>{
console.log('注册一个钩子,在组件被挂载之前被调用。onBeforeMount')
})
onMounted(()=>{
console.log('注册一个回调函数,在组件挂载完成后执行。onMounted')
})
onBeforeUpdate(()=>{
console.log('注册一个钩子,在组件即将因为响应式状态变更而更新其 DOM 树之前调用。onBeforeUpdate')
})
onUpdated(()=>{
console.log('注册一个回调函数,在组件因为响应式状态变更而更新其 DOM 树之后调用。onUpdated')
})
onBeforeUnmount(()=>{
console.log('注册一个钩子,在组件实例被卸载之前调用。onBeforeUnmount')
})
onUnmounted(()=>{
console.log('注册一个回调函数,在组件实例被卸载之后调用。onUnmounted')
})
onErrorCaptured(() => {
console.log('注册一个钩子,在捕获了后代组件传递的错误时调用。onErrorCaptured')
})
onRenderTracked(() => {
console.log('注册一个调试钩子,当组件渲染过程中追踪到响应式依赖时调用。onRenderTracked')
})
onRenderTriggered(() => {
console.log('注册一个调试钩子,当响应式依赖的变更触发了组件渲染时调用。onRenderTriggered')
})
onActivated(() => {
console.log('注册一个回调函数,若组件实例是 <KeepAlive> 缓存树的一部分,当组件被插入到 DOM 中时调用。onActivated')
})
onDeactivated(() => {
console.log('注册一个回调函数,若组件实例是 <KeepAlive> 缓存树的一部分,当组件从 DOM 中被移除时调用。onDeactivated')
})
onServerPrefetch(() => {
console.log('注册一个异步函数,在组件实例在服务器上被渲染之前调用。onServerPrefetch')
})
return { }
},
})
</script>
Vue Router
安装
npm install vue-router@4
router-link
router-link 实现路由之间的跳转
将导航栏的链接href改为对应的路由
<a class="navbar-brand" href="/pk/">King Of Bots</a>
router-link标签—-前端渲染,可以实现点击后不刷新
<router-link class="navbar-brand" :to="{name:'home'}">King Of Bots</router-link>
router-view
router-view 当你的路由path 与访问的地址相符时,会将指定的组件替换该 router-view
App.vue中
<template>
<NavBar/>
<router-view />
</template>
动态路由
很多时候,我们需要将给定匹配模式的路由映射到同一个组件。例如,我们可能有一个 User
组件,它应该对所有用户进行渲染,但用户 ID 不同。在 Vue Router 中,我们可以在路径中使用一个动态字段来实现,我们称之为 路径参数 :
const User = {
template: '<div>User</div>',
}
// 这些都会传递给 `createRouter`
const routes = [
// 动态字段以冒号开始
{ path: '/users/:id', component: User },
]
现在像 /users/johnny
和 /users/jolyne
这样的 URL 都会映射到同一个路由。
路径参数 用冒号 :
表示。当一个路由被匹配时,它的 params 的值将在每个组件中以 this.$route.params
的形式暴露出来。因此,我们可以通过更新 User
的模板来呈现当前的用户 ID:
const User = {
template: '<div>User {{ $route.params.id }}</div>',
}
嵌套路由
要将组件渲染到这个嵌套的 router-view
中,我们需要在路由中配置 children
:
const routes = [
{
path: '/user/:id',
component: User,
children: [
{
// 当 /user/:id/profile 匹配成功
// UserProfile 将被渲染到 User 的 <router-view> 内部
path: 'profile',
component: UserProfile,
},
{
// 当 /user/:id/posts 匹配成功
// UserPosts 将被渲染到 User 的 <router-view> 内部
path: 'posts',
component: UserPosts,
},
],
},
]
注意,以 /
开头的嵌套路径将被视为根路径。这允许你利用组件嵌套,而不必使用嵌套的 URL。
如你所见,children
配置只是另一个路由数组,就像 routes
本身一样。因此,你可以根据自己的需要,不断地嵌套视图。
此时,按照上面的配置,当你访问 /user/eduardo
时,在 User
的 router-view
里面什么都不会呈现,因为没有匹配到嵌套路由。也许你确实想在那里渲染一些东西。在这种情况下,你可以提供一个空的嵌套路径:
const routes = [
{
path: '/user/:id',
component: User,
children: [
// 当 /user/:id 匹配成功
// UserHome 将被渲染到 User 的 <router-view> 内部
{ path: '', component: UserHome },
// ...其他子路由
],
},
]
rouer配置
直接输入根地址重定向到pk
{
path: "/",
name: "home",
redirect:"/pk/"
},
重定向到404
{
path: "/:catchAll(.*)",
redirect:"/404/"
}
动态路由
const routes =[
//动态字段以冒号开始
{
path: '/users/:id',
component: User
},
]
import { createRouter, createWebHistory } from 'vue-router'
import PkIndexView from "../views/pk/PkIndexView"
import RecordIndexView from "../views/record/RecordIndexView"
import NotFound from "../views/error/NotFound"
const routes = [
{
path: "/",
name: "home",
redirect:"/pk/"
},
{
path: "/pk/",
name:"pk_index",
component:PkIndexView,
},
{
path: "/record/",
name:"record_index",
component:RecordIndexView,
},
{
path: "/404/",
name:"404",
component:NotFound,
},
{
path: "/:catchAll(.*)",
redirect:"/404/"
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
<hr< body=””></hr<>
不同的历史模式
history模式
hash 模式是用 createWebHashHistory()
创建的:
路径带#号
它在内部传递的实际 URL 之前使用了一个哈希字符(#
)。由于这部分 URL 从未被发送到服务器,所以它不需要在服务器层面上进行任何特殊处理。不过,它在 SEO 中确实有不好的影响。如果你担心这个问题,可以使用 HTML5 模式。
http://192.168.85.1:3000/#/page
const router = createRouter({
history: createWebHashHistory(),
routes,
})
HTML5 模式
用 createWebHistory()
创建 HTML5 模式,推荐使用这个模式:
路径不带#号,但需要后端进行配置(如nginx配置)
当使用这种历史模式时,URL 会看起来很 “正常”,例如 https://example.com/user/id
。漂亮!
不过,问题来了。由于我们的应用是一个单页的客户端应用,如果没有适当的服务器配置,用户在浏览器中直接访问 https://example.com/user/id
,就会得到一个 404 错误。这就丑了。
不用担心:要解决这个问题,你需要做的就是在你的服务器上添加一个简单的回退路由。如果 URL 不匹配任何静态资源,它应提供与你的应用程序中的 index.html
相同的页面。漂亮依旧!
const router = createRouter({
history: createWebHistory(),
routes,
})
服务器配置示例
注意:以下示例假定你正在从根目录提供服务。如果你部署到子目录,你应该使用Vue CLI 的 publicPath
配置和相关的路由器的 base
属性。你还需要调整下面的例子,以使用子目录而不是根目录(例如,将RewriteBase/
替换为 RewriteBase/name-of-your-subfolder/
)。
vue.config.js
module.exports = {
publicPath: process.env.NODE_ENV === 'production'
? '/production-sub-path/'
: '/'
}
Apache
<IfModule mod_negotiation.c>
Options -MultiViews
</IfModule>
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
</IfModule>
也可以使用 FallbackResource
代替 mod_rewrite
。
nginx
location / {
try_files $uri $uri/ /index.html;
}
原生 Node.js
const http = require('http')
const fs = require('fs')
const httpPort = 80
http
.createServer((req, res) => {
fs.readFile('index.html', 'utf-8', (err, content) => {
if (err) {
console.log('We cannot open "index.html" file.')
}
res.writeHead(200, {
'Content-Type': 'text/html; charset=utf-8',
})
res.end(content)
})
})
.listen(httpPort, () => {
console.log('Server listening on: http://localhost:%s', httpPort)
})
动态路由
useRoute()
route是一个跳转的路由对象,每一个路由都会有一个route对象,是一个局部的对象,可以获取对应的name,path,params,query等:
matched: 数组,包含当前匹配的路径中所包含的所有片段所对应的配置参数对象
name: 当前路径的名字,如果没有使用具名路径,则名字为空。
params: 对象,包含路由中的动态片段和全匹配片段的键值对
path: 字符串,等于当前路由对象的路径,会被解析为绝对路径,如 “/home/news”
query: 对象,包含路由中查询参数的键值对。 例如,对于 /home/news/detail/01?favorite=yes ,会得到$route.query.favorite => ‘yes’
<script>
import { useRoute } from 'vue-router'
import { computed } from 'vue'
export default {
setup(){
const route = useRoute();
let route_name = computed(() => route.name);
return {
route_name
}
}
}
</script>
根据路由判断是否active,是否高亮
<router-link :class="route_name=='pk_index'?'nav-link active':'nav-link' " :to="{name:'pk_index'}">对战</router-link>
axios
安装
npm install axios
GET
<script>
import { axios } from 'axios'
export default {
setup(){
axios.get('127.0.0.1:5000/test').then(res)=>{
console.log(res);
}
return { }
}
}
</script>
POST
<script>
import { axios } from 'axios'
export default {
setup(){
axios.post('127.0.0.1:5000/test',{
date:"hhhhh",
data2:"aaaaa"
}).then(res)=>{
console.log(res);
}
return { }
}
}
</script>
多并发请求
function getUserAccount() {
return axios.get('/user/12345');
}
function getUserPermissions() {
return axios.get('/user/12345/permissions');
}
Promise.all([getUserAccount(), getUserPermissions()])
.then(function (results) {
const acct = results[0];
const perm = results[1];
});
vite代理解决跨域
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
server: {
port: 3000,
open: true,
// 配置代理
proxy: {
// 请求的路径前缀只要是 /testaxios 就会被拦截走这个代理
'/testaxios': {
/**
* 请求的目标资源再经过替换成 /httphwm/getList 后,
* 会加上 http://127.0.0.1:9693 这个前缀,
* 最后请求的URL为: http://127.0.0.1:9693/httphwm/getList
*/
target: 'http://127.0.0.1:9693',//被替换端的服务器地址
ws: true,
changeOrigin: true,
// 拦截到的请求路径 testaxios/httphwm/getList,/testaxios会被替换成空
rewrite: (path) => path.replace(/^\/testaxios/, ''),
},
},
},
vuex
安装
npm install vuex@next --save
结构
vuex
:存储全局状态,全局唯一。
state
: 存储所有数据,可以用modules
属性划分成若干模块getters
:根据state
中的值计算新的值mutations
:所有对state
的修改操作都需要定义在这里,不支持异步,可以通过$store.commit()
触发actions
:定义对state
的复杂修改操作,支持异步,可以通过$store.dispatch()
触发。注意不能直接修改state
,只能通过mutations
修改state
。modules
:定义state
的子模块
使用
安装 Vuex 之后,让我们来创建一个 store。创建过程直截了当——仅需要提供一个初始 state 对象和一些 mutation:
import { createApp } from 'vue'
import { createStore } from 'vuex'
// 创建一个新的 store 实例
const store = createStore({
state () {
return {
count: 0
}
},
mutations: {
increment (state) {
state.count++
}
}
})
const app = createApp({ /* 根组件 */ })
// 将 store 实例作为插件安装
app.use(store)
现在,你可以通过 store.state
来获取状态对象,并通过 store.commit
方法触发状态变更:
store.commit('increment')
console.log(store.state.count) // -> 1
在 Vue 组件中, 可以通过 this.$store
访问store实例。现在我们可以从组件的方法提交一个变更:
methods: {
increment() {
this.$store.commit('increment')
console.log(this.$store.state.count)
}
}
再次强调,我们通过提交 mutation 的方式,而非直接改变 store.state.count
,是因为我们想要更明确地追踪到状态的变化。这个简单的约定能够让你的意图更加明显,这样你在阅读代码的时候能更容易地解读应用内部的状态改变。此外,这样也让我们有机会去实现一些能记录每次状态改变,保存状态快照的调试工具。有了它,我们甚至可以实现如时间穿梭般的调试体验。
由于 store 中的状态是响应式的,在组件中调用 store 中的状态简单到仅需要在计算属性中返回即可。触发变化也仅仅是在组件的 methods 中提交 mutation。
赞赏 微信赞赏
支付宝赞赏
好厉害的小哥哥!
是爱学习的小哥哥一枚呀~