Vue3
目录结构#
模版语法#
使用 JavaScript 表达式#
在我们的模板中,我们一直都只绑定简单的 property 键值,Vue.js 都提供了完全的 JavaScript 表达式支持
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
这些表达式会在当前活动实例的数据作用域下作为 JavaScript 被解析。有个限制就是,每个绑定都只能包含单个表达式,所以下面的例子都不会生效。
<!-- 这是语句,不是表达式:-->
{{ var a = 1 }}
<!-- 流程控制也不会生效,请使用三元表达式 -->
{{ if (ok) { return message } }}
原始 HTML#
双大括号会将数据解释为普通文本,而非 HTML 代码。为了输出真正的 HTML,你需要使用v-html
指令
<p>Using mustaches: {{ rawHtml }}</p>
<p>Using v-html directive: <span v-html="rawHtml"></span></p>
data(){
return{
rawHtml:"<a href='https://www.itbaizhan.com'>百战</a>"
}
}
属性 Attribute#
Mustache 语法不能在 HTML 属性中使用,然而,可以使用 v-bind
指令
<div v-bind:id="dynamicId"></div>
data(){
return{
dynamicId:1001
}
}
温馨提示
v-bind:
可以简写成:
条件渲染#
v-if#
v-if
指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回 true
值的时候被渲染。
<p v-if="flag">我是孙猴子</p>
data() {
return {
flag: true
}
}
v-else#
你可以使用 v-else
指令来表示 v-if
的“else 块”
<p v-if="flag">我是孙猴子</p>
<p v-else>你是傻猴子</p>
data() {
return {
flag: false
}
}
v-show#
另一个用于条件性展示元素的选项是 v-show
指令
<h1 v-show="ok">Hello!</h1>
v-if
vs v-show
的区别#
v-if
是“真正”的条件渲染,因为它会确保在切换过程中,条件块内的事件监听器和子组件适当地被销毁和重建。
v-if
也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
相比之下,v-show
就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。
一般来说,v-if
有更高的切换开销,而 v-show
有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show
较好;如果在运行时条件很少改变,则使用 v-if
较好
Class 与 Style 绑定#
绑定 HTML class#
普通对象使用#
Style绑定#
计算属性的使用#
列表渲染#
v-for#
用 v-for
把一个数组映射为一组元素#
我们可以用 v-for
指令基于一个数组来渲染一个列表。v-for
指令需要使用 item in items
形式的特殊语法,其中 items 是源数据数组,而 item
则是被迭代的数组元素的别名。
<ul>
<li v-for="item in items">{{ item.message }}</li>
</ul>
data() {
return {
items: [{ message: 'Foo' }, { message: 'Bar' }]
}
}
维护状态#
当 Vue 正在更新使用 v-for
渲染的元素列表时,它默认使用“就地更新”的策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素,并且确保它们在每个索引位置正确渲染。
为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一的 key
attribute:
<div v-for="(item,index) in items" :key="item.id|index">
<!-- 内容 -->
</div>
数组变化侦测#
Vue 能够侦听响应式数组的变更方法,并在它们被调用时触发相关的更新。这些变更方法包括:
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
v-for 与对象#
事件处理#
监听事件#
我们可以使用 v-on
指令 (简写为 @
) 来监听 DOM 事件,并在事件触发时执行对应的 JavaScript。用法:v-on:click="handler"
或 @click="handler"
。
事件处理器 (handler) 的值可以是:
-
内联事件处理器:事件被触发时执行的内联 JavaScript 语句 (与
onclick
类似)。<button @click="counter += 1">Add 1</button>
data() { return { counter: 0 } }
-
方法事件处理器:一个指向组件上定义的方法的属性名或是路径。
然而许多事件处理逻辑会更为复杂,所以直接把 JavaScript 代码写在
v-on
指令中是不可行的。因此v-on
还可以接收一个需要调用的方法名称。<button @click="greet">Greet</button>
methods: { greet(event) { // `event` 是原生 DOM event if (event) { alert(event.target.tagName) } } }
事件修饰符#
按键修饰符#
计算属性#
侦听器#
表单事件#
复选框#
单选框#
选项框#
多选框#
修饰符#
模版引用#
dom操作
组件基础#
使用组件#
<script>
import ButtonCounter from './ButtonCounter.vue'
export default {
components: {
ButtonCounter
}
}
</script>
<template>
<h1>Here is a child component!</h1>
<ButtonCounter />
</template>
组件注册#
父传子组件传值prop#
组件与组件之间是需要存在交互的,否则完全没关系,组件的意义就很小了
Prop
是你可以在组件上注册的一些自定义 attribute
<my-componentVue :title="标题"/>
<template>
<h3>单文件组件</h3>
<p>{{ title }}</p>
</template>
<script>
export default {
name:"MyComponent",
props:{
title:{
type:String,
default:""
}
}
}
</script>
动态数据(父传子 )#
Prop 类型#
Prop传递参数其实是没有类型限制的 prop是只读的
props: {
title: String,
likes: Number,
isPublished: Boolean,
commentIds: Array,
author: Object,
callback: Function
}
温馨提示
数据类型为数组或者对象的时候,默认值是需要返回工厂模式
子组件向父组件传值#
组件事件#
子组件:
父组件:
父子组件的访问方式#
组合事件和v-model一起使用#
组件数据传递prop 子传父#
子组件#
父组件#
插槽#
双标签 ,之前的是单标签
父组件:
子组件:
通过插槽来分配内容#
具名插槽#
子组件有多个插槽
父组件:
子组件:
简写:
渲染作用域#
备用内容#
作用插槽域#
插槽使用父子组件的数据:#
普通方式:#
父组件:
子组件:
具名方式#
父组件:
子组件:
组件之间的跨级通信(依赖注入)#
将数据传递得更深:
privide 和 inject#
祖先:
孙子:
生命周期钩子#
应用#
获取dom:
异步组件:#
vue应用#
组合式API#
Vue3新特性1#
Vue3是目前Vue的最新版本,自然也是新增了很多新特性
六大亮点#
- Performance:性能更比Vue 2.0强。
- Tree shaking support:可以将无用模块“剪辑”,仅打包需要的。
- Composition API:组合API
- Fragment, Teleport, Suspense:“碎片”,Teleport即Protal传送门,“悬念”
- Better TypeScript support:更优秀的Ts支持
- Custom Renderer API:暴露了自定义渲染API
ref或者reactive#
在2.x中通过组件data的方法来定义一些当前组件的数据
data() {
return {
name: 'iwen',
list: [],
}
}
在3.x中通过ref或者reactive创建响应式对象
import { ref,reactive } from "vue"
export default {
name: 'HelloWorld',
setup(){
const name = ref("iwen")
const state = reactive({
list:[]
})
return{
name,
state
}
}
}
methods中定义的方法写在setup()#
在2.x中methods来定义一些当前组件内部方法
methods:{
http(){}
}
在3.x中直接在setup方法中定义并return
setup() {
const http = ()=>{
// do something
}
return {
http
};
}
setup()中使用props和context#
在2.x中,组件的方法中可以通过this获取到当前组件的实例,并执行data变量的修改,方法的调用,组件的通信等等,但是在3.x中,setup()在beforeCreate和created时机就已调用,无法使用和2.x一样的this,但是可以通过接收setup(props,ctx)的方法,获取到当前组件的实例和props
export default {
props: {
name: String,
},
setup(props,ctx) {
console.log(props.name)
ctx.emit('event')
},
}
响应式 API#
- 响应式 API:例如
ref()
和reactive()
,使我们可以直接创建响应式状态、计算属性和侦听器。 - ref() 基础类型
- reactive() 引用类型
torefs 响应式#
在setup 中使用watch#
基础类型#
引用类型 watchEffect#
使用watch#
生命周期函数#
Vue3新特性2#
在setup中使生命周期函#
你可以通过在生命周期钩子前面加上 “on” 来访问组件的生命周期钩子。
下表包含如何在 setup () 内部调用生命周期钩子
Options API | Hook inside setup |
---|---|
beforeCreate | Not needed* |
created | Not needed* |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeUnmount | onBeforeUnmount |
unmounted | onUnmounted |
export default {
setup() {
// mounted
onMounted(() => {
console.log('Component is mounted!')
})
}
}
Provide / Inject#
- provide() 和 inject() 可以实现嵌套组件之间的数据传递。
- 这两个函数只能在 setup() 函数中使用。
- 父级组件中使用 provide() 函数向下传递数据。
- 子级组件中使用 inject() 获取上层传递过来的数据。
- 不限层级
// 父组件
import { provide } from "vue"
setup() {
provide("customVal", "我是父组件向子组件传递的值");
}
// 子组件
import { inject } from "vue"
setup() {
const customVal = inject("customVal");
return {
customVal
}
}
Fragment#
Fragment翻译为:“碎片”
- 不再限于模板中的单个根节点
<template>
<img alt="Vue logo" src="./assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js App" />
</template>
setup函数中的参数prop#
父传子
setup函数中的参数 context#
听懵逼了、
provide和inject#
< script setup>#
script setup 是在单文件组件 (SFC) 中使用组合式 API 的编译时语法糖。
vue-router#
Vue引入路由配置(初级)#
在Vue中,我们可以通过vue-router
路由管理页面之间的关系
Vue Router 是 Vue.js 的官方路由。它与 Vue.js 核心深度集成,让用 Vue.js 构建单页应用变得轻而易举
在Vue中引入路由#
第一步:安装路由 npm install --save vue-router
第二步:配置独立的路由文件
// index.js
import { createRouter, createWebHashHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
const routes = [
{
path: '/',
name: 'home',
component: HomeView
},
{
path: '/about',
name: 'about',
component: () => import('../views/AboutView.vue')
}
]
const router = createRouter({
history: createWebHashHistory(),
routes
})
export default router
第三步:引入路由到项目
// main.js
import router from './router'
app.use(router)
第四步:指定路由显示入口 <router-view/>
第五步:指定路由跳转
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
路由传递参数#
页面跳转过程中,是可以携带参数的,这也是很常见的业务
例如:在一个列表项,点击进入查看每个列表项的详情
第一步:在路由配置中指定参数的key
{
path:"/list/:name",
name:"list",
component:() => import("../views/ListView.vue")
}
第二步:在跳转过程中携带参数
<li><router-link to="/list/内蒙">内蒙旅游十大景区</router-link></li>
<li><router-link to="/list/北京">北京旅游十大景区</router-link></li>
<li><router-link to="/list/四川">四川旅游十大景区</router-link></li>
第三步:在详情页面读取路由携带的参数
<p>{{ $route.params.name }}城市旅游景区详情</p>
基础使用#
路由表#
index.js
import {
createRouter,
createWebHashHistory,
createWebHistory
} from 'vue-router'
// 1. 定义路由组件.
// 也可以从其他文件导入
// 静态导入
// import Home from '../views/Home.vue'
import About from '../views/About.vue'
import User from '../views/User.vue'
import NotFound from '../views/NotFound.vue'
import News from '../views/News.vue'
import Parent from '../views/Parent.vue'
import StyleOne from '../views/StyleOne.vue'
import StyleTwo from '../views/StyleTwo.vue'
import Page from '../views/Page.vue'
import ShopTop from '../views/ShopTop.vue'
import ShopMain from '../views/ShopMain.vue'
import ShopFooter from '../views/ShopFooter.vue'
// 路由懒加载,用到时再加载
const Home=()=>import('../views/Home.vue')
// 2. 定义一些路由
// 每个路由都需要映射到一个组件。
// 我们后面再讨论嵌套路由。
const routes = [{
path: "/",
// 重定向
// redirect:'/home'
// 命名路由
// redirect:{name:"home"}
// 方法
redirect: (to) => {
// console.log(to);
return {
path: "/home"
}
}
},
{
path: '/home',
name: "home",
component: Home
},
{
path: '/about',
component: About,
// 每路守卫(路由独享的守卫)
beforeEnter:(to,from,next)=>{//token
console.log(to);
console.log(from);
if(123 === 123453){
next()
}
}
},
{
// 动态路由
path: '/user/:id',
component: User,
props: true
},
{
// 动态路由的参数一定是数字
// path: "/news/:id(\\d+)",
// 有多个参数 +
// path: "/news/:id+",
// 参数可有可无 * ,参数可以重复叠加
name: "news",
path: "/news/:id*",
// 参数可有可无 ? ,但是参数不可以重复叠加
// path: "/news/:id?",
component: News
},
{
path: "/parent",
alias: ['/father', '/fuqin'], //起别名
component: Parent,
children: [{
path: "styleone",
component: StyleOne
},
{
path: "styletwo",
component: StyleTwo,
}
],
},
{
path: "/page",
component: Page
},
{
path: "/shop/:id",
components: {
default: ShopMain,
// 它们与 `<router-view>` 上的 `name` 属性匹配
ShopTop: ShopTop,
ShopFooter: ShopFooter
},
props: {
default: true,
ShopFooter: true,
ShopTop: false
}
},
{
// 404页面
//使用正则的方式,匹配任意的
path: '/:path(.*)',
component: NotFound
},
]
// 3. 创建路由实例并传递 `routes` 配置
// 你可以在这里输入更多的配置,但我们在这里
// 暂时保持简单
const router = createRouter({
// 4. 内部提供了 history 模式的实现。为了简单起见,我们在这里使用 hash 模式。
// history: createWebHashHistory(),
// history模式,二者区别,有无#
history: createWebHistory(),
routes, // `routes: routes` 的缩写
})
// 全局守卫
// router.beforeEach((to,from,next)=>{
// console.log(to);
// console.log(from);
// next()//通行证
// })
export default router
使用#
<script>
export default{
data(){
return {
list:[]
}
}
}
</script>
<template>
<div>
<!-- vue-router是基于路由和组件的,路由是用来设定访问路径,将路径和组件映射起来, -->
<h1>Hello App!</h1>
<router-view name="ShopTop"></router-view>
<router-view></router-view>
<p>
<!--使用 router-link 组件进行导航 -->
<!--通过传递 `to` 来指定链接 -->
<!--`<router-link>` 将呈现一个带有正确 `href` 属性的 `<a>` 标签-->
<!-- 使用一个自定义组件 router-link 来创建链接。这使得 Vue Router 可以在不重新加载页面的情况下更改 URL,处理 URL 的生成以及编码 -->
<!-- to="路径" -->
<router-link to="/">Go to Home</router-link>
<router-link to="/about">Go to About</router-link>
<router-link to="/user/123">Go to User</router-link>
<!-- <router-link to="/news/456">Go to News</router-link> -->
<router-link :to="{name:'news',params:{id:456}}">Go to News</router-link>
<router-link to="/parent">Go to Parent</router-link>
<router-link to="/page">Go to Page</router-link>
</p>
<!-- 路由出口,占位符 -->
<!-- 路由匹配到的组件将渲染在这里 -->
<router-view name="ShopFooter"></router-view>
</div>
</template>
<style>
</style>
挂载#
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
const app=createApp(App)
app.use(router)
app.mount('#app')
动态路由#
路由表
{
// 动态路由
path: '/user/:id',
component: User,
props: true
},
组件
<template>
<div>用户</div>
</template>
<!---<script>
export default {
props:['id'],
mounted(){
// $route表示当前活跃的路由对象
console.log(this.$route.params.id);
console.log(this.id);
}
}
</script>
--->
<script setup>
import {useRoute} from 'vue-router'
console.log(useRoute().params.id);
const props= defineProps({
id:String
})
console.log(props.id);
</script>
404路由#
路由表
{
// 404页面
//使用正则的方式,匹配任意的
path: '/:path(.*)',
component: NotFound
},
路由正则和重复参数#
路由正则 重复参数
{
// 动态路由的参数一定是数字
// path: "/news/:id(\\d+)",
// 有多个参数 +
// path: "/news/:id+",
// 参数可有可无 * ,参数可以重复叠加
name: "news",
path: "/news/:id*",
// 参数可有可无 ? ,但是参数不可以重复叠加
// path: "/news/:id?",
component: News
},
嵌套路由配置#
路由嵌套是非常常见的需求
第一步:创建子路由要加载显示的页面
第二步:在路由配置文件中添加子路由配置
{
path:"/news",
name:"news",
redirect:"/news/baidu",
component:() => import("../views/NewsView.vue"),
children:[
{
path:"baidu",
component:() => import("../views/NewsList/BaiduNews.vue"),
},
{
path:"wangyi",
component:() => import("../views/NewsList/WangyiNews.vue"),
}
]
}
第三步:指定子路由显示位置<router-view></router-view>
第四步:添加子路由跳转链接
<router-link to="/news/baidu">百度新闻</router-link> |
<router-link to="/news/wangyi">网易新闻</router-link>
第五步:重定向配置 redirect:"/news/baidu"
{
path: "/parent",
alias: ['/father', '/fuqin'], //起别名
component: Parent,
children: [{
path: "styleone",
component: StyleOne
},
{
path: "styletwo",
component: StyleTwo,
}
],
},
js跳转#
<template>
<div>
<h2>Page页面</h2>
<button @click="goPage">跳转页面</button>
</div>
</template>
<script>
export default {
methods: {
goPage: function () {
// console.log(this.$router);
// if(123==1221343){
// this.$router.push('/')
// 通过传入对象
// this.$router.push({path:"/"})
// 带参数
// this.$router.push({path:"/user/123456"})
// this.$router.push({name:"news",params:{id:123456}})
// 带问号
this.$router.push({path:"/about",query:{name:"zhangsan"}})
//替换当前位置
// this.$router.push({ path: "/about", query: { name: "zhangsan" },replace:true });
// this.$router.replace({path:"/about",query:{name:"zhangsan"}})
// }
},
},
};
</script>
接受
<template>
<div>
<div>about</div>
<button @click="goBack">后退</button>
</div>
</template>
<script>
export default {
mounted(){
console.log(this.$route.query.name);
},
methods:{
goBack(){
// 前进,传入为正值,后退,传入的值则为负值
// this.$router.go(-1)
this.$router.back()//后退,等于go(-1)
this.$router.forword()//前进,等于go(1)
}
},
}
</script>
替换页面#
export default {
methods: {
goPage: function () {
// console.log(this.$router);
// if(123==1221343){
// this.$router.push({ path: "/about", query: { name: "zhangsan" },replace:true });
// this.$router.replace({path:"/about",query:{name:"zhangsan"}})
// }
},
},
};
历史页面#
<template>
<div>
<div>about</div>
<button @click="goBack">后退</button>
</div>
</template>
<script>
export default {
mounted(){
console.log(this.$route.query.name);
},
methods:{
goBack(){
// 前进,传入为正值,后退,传入的值则为负值
// this.$router.go(-1)
this.$router.back()//后退,等于go(-1)
this.$router.forword()//前进,等于go(1)
}
},
}
</script>
命名路由#
命名
{
// 动态路由的参数一定是数字
// path: "/news/:id(\\d+)",
// 有多个参数 +
// path: "/news/:id+",
// 参数可有可无 * ,参数可以重复叠加
name: "news",
path: "/news/:id*",
// 参数可有可无 ? ,但是参数不可以重复叠加
// path: "/news/:id?",
component: News
},
使用
<router-link :to="{name:'news',params:{id:456}}">Go to News</router-link>
命名视图#
一个页面显示 多个组件
定义
{
path: "/shop/:id",
components: {
default: ShopMain,
// 它们与 `<router-view>` 上的 `name` 属性匹配
ShopTop: ShopTop,
ShopFooter: ShopFooter
},
props: {
default: true,
ShopFooter: true,
ShopTop: false
}
},
使用
<template>
<div>
<!-- vue-router是基于路由和组件的,路由是用来设定访问路径,将路径和组件映射起来, -->
<h1>Hello App!</h1>
<router-view name="ShopTop"></router-view>
<router-view></router-view>
<!-- 路由出口,占位符 -->
<!-- 路由匹配到的组件将渲染在这里 -->
<router-view name="ShopFooter"></router-view>
</div>
</template>
重定向和别名#
重定向
path: "/",
// 重定向
// redirect:'/home'
// 命名路由
// redirect:{name:"home"}
// 方法
redirect: (to) => {
// console.log(to);
return {
path: "/home"
}
}
别名
{
path: "/parent",
alias: ['/father', '/fuqin'], //起别名
component: Parent,
children: [{
path: "styleone",
component: StyleOne
},
{
path: "styletwo",
component: StyleTwo,
}
],
},
路由组件传参#
不同的历史模式#
Hash 模式
import { createRouter, createWebHashHistory } from 'vue-router'
const router = createRouter({
history: createWebHashHistory(),
routes: [
//...
],
})
html5
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(),
routes: [
//...
],
})
路由守卫#
就是路由跳转 可以进行判断的地方
全局路由#
// 你可以在这里输入更多的配置,但我们在这里
// 暂时保持简单
const router = createRouter({
// 4. 内部提供了 history 模式的实现。为了简单起见,我们在这里使用 hash 模式。
// history: createWebHashHistory(),
// history模式,二者区别,有无#
history: createWebHistory(),
routes, // `routes: routes` 的缩写
})
// 全局守卫
// router.beforeEach((to,from,next)=>{
// console.log(to);
// console.log(from);
// next()//通行证
// })
每路守卫#
{
path: '/about',
component: About,
// 每路守卫(路由独享的守卫)
beforeEnter:(to,from,next)=>{//token
console.log(to);
console.log(from);
if(123 === 123453){
next()
}
}
},
组件类的守卫#
<template>
<div>新闻</div>
</template>
<script>
export default {
data() {
return {
age: 18,
};
},
beforeRouteEnter(to, from,next) {//拿不到实例对象,通过next的回调函数
console.log(to);
console.log(from);
next((vm)=>{
console.log(vm.age);
})
console.log("路由进入组件之前");
},
beforeRouteUpdate() {
console.log("路由更新组件之前");
},
beforeRouteLeave() {
console.log("路由离开组件之前");
},
};
</script>
路由懒加载#
// 路由懒加载,用到时再加载
const Home=()=>import('../views/Home.vue')
状态管理#
状态
== 数据
在不使用 vuex的情况下 使用 // provide/inject 跨级通信 进行数据管理
导入数据#
<script>
import Home from "./views/Home.vue";
import store from "./store";
// vue3中如何设置状态管理
// provide/inject 跨级通信
export default {
provide: {
store,
},
components: {
Home,
},
};
</script>
<template>
<Home />
</template>
<style>
</style>
fetch获取数据#
// http://localhost:3001/banner
// fetch//原生JS,是http数据请求的一种方式
created() {
// fetch返回promise对象
// fetch('http://localhost:3001/banner').then((res)=>{//默认执行get请求
// //json()将响应的body,解析json的promise
// // console.log(res.json());
// return res.json()
// }).then((res)=>{
// console.log(res);
// // this.bannersList=res.banners
// this.store.updateBannersList(res.banners)
// })
// axios:基于promise的http库
// axios.get('http://localhost:3001/banner').then((res)=>{
// console.log(res);
// })
// https://i.maoyan.com/api/mmdb/movie/v3/list/hot.json?ct=%E9%95%BF%E6%B2%99&ci=70&channelId=4
// 跨域请求数据,浏览器同源策略的保护机制,通过proxy实现跨域请求数据
axios.get("/path/api/mmdb/movie/v3/list/hot.json?ct=%E9%95%BF%E6%B2%99&ci=70&channelId=4").then((res) => {
console.log(res);
});
},
Axios网络请求#
Axios 是一个基于 promise 的网络请求库
安装#
Axios的应用是需要单独安装的 npm install --save axios
引入#
组件中引入: import axios from "axios"
全局引用:
import axios from "axios"
const app = createApp(App);
app.config.globalProperties.$axios = axios
app.mount('#app')
// 在组件中调用
this.$axios
网络请求基本示例#
get请求
axios({
method: "get",
url: "http://iwenwiki.com/api/blueberrypai/getChengpinDetails.php"
}).then(res => {
console.log(res.data);
})
// axios:基于promise的http库
// axios.get('http://localhost:3001/banner').then((res)=>{
// console.log(res);
// })
// https://i.maoyan.com/api/mmdb/movie/v3/list/hot.json?ct=%E9%95%BF%E6%B2%99&ci=70&channelId=4
// 跨域请求数据,浏览器同源策略的保护机制,通过proxy实现跨域请求数据
axios.get("/path/api/mmdb/movie/v3/list/hot.json?ct=%E9%95%BF%E6%B2%99&ci=70&channelId=4").then((res) => {
console.log(res);
});
},
post请求
温馨提示
post请求参数是需要额外处理的
- 安装依赖:
npm install --save querystring
- 转换参数格式:
qs.stringify({})
axios({
method:"post",
url:"http://iwenwiki.com/api/blueberrypai/login.php",
data:qs.stringify({
user_id:"iwen@qq.com",
password:"iwen123",
verification_code:"crfvw"
})
}).then(res =>{
console.log(res.data);
})
快捷方案#
get请求
axios.get("http://iwenwiki.com/api/blueberrypai/getChengpinDetails.php")
.then(res =>{
console.log(res.data);
})
post请求
axios.post("http://iwenwiki.com/api/blueberrypai/login.php", qs.stringify({
user_id: "iwen@qq.com",
password: "iwen123",
verification_code: "crfvw"
}))
.then(res => {
console.log(res.data);
})
Axios网络请求封装#
在日常应用过程中,一个项目中的网络请求会很多,此时一般采取的方案是将网络请求封装起来
在src
目录下创建文件夹utils
,并创建文件request
,用来存储网络请求对象 axios
import axios from "axios"
import qs from "querystring"
const errorHandle = (status,info) => {
switch(status){
case 400:
console.log("语义有误");
break;
case 401:
console.log("服务器认证失败");
break;
case 403:
console.log("服务器拒绝访问");
break;
case 404:
console.log("地址错误");
break;
case 500:
console.log("服务器遇到意外");
break;
case 502:
console.log("服务器无响应");
break;
default:
console.log(info);
break;
}
}
const instance = axios.create({
timeout:5000
})
instance.interceptors.request.use(
config =>{
if(config.method === "post"){
config.data = qs.stringify(config.data)
}
return config;
},
error => Promise.reject(error)
)
instance.interceptors.response.use(
response => response.status === 200 ? Promise.resolve(response) : Promise.reject(response),
error =>{
const { response } = error;
errorHandle(response.status,response.info)
}
)
export default instance;
在src
目录下创建文件夹api
,并创建文件index
和path
分别用来存放网络请求方法和请求路径
// path.js
const base = {
baseUrl:"http://iwenwiki.com",
chengpin:"/api/blueberrypai/getChengpinDetails.php"
}
export default base
// index.js
import path from "./path"
import axios from "../utils/request"
export default {
getChengpin(){
return axios.get(path.baseUrl + path.chengpin)
}
}
在组件中直接调用网络请求
import api from "../api/index"
api.getChengpin().then(res =>{
console.log(res.data);
})
vite通过proxy代理解决跨域问题#
vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
server:{//中转服务器
proxy:{//通过代理实现跨域
//https://i.maoyan.com
'/path':{
target:'https://i.maoyan.com',//替换的服务端地址
changeOrigin:true,//开启代理,允许跨域
rewrite:path=>path.replace(/^\/path/,'')//设置重写的路径
}
}
}
})
通过vue_cli创建项目#
Vue状态管理(Vuex)#
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
简单来说,状态管理可以理解成为了更方便的管理组件之间的数据交互,提供了一个集中式的管理方案,任何组件都可以按照指定的方式进行读取和改变数据
引入Vuex的步骤#
第一步:安装Vuex npm install --save vuex
第二步:配置Vuex文件
import { createStore } from 'vuex'
export default createStore({
state: {
counter:0
}
})
第三步:在主文件中引入Vuex
import store from './store'
app.use(store)
第四步:在组件中读取状态
<p>counter:{{ $store.state.counter }}</p>
// 或者
import { mapState } from 'vuex';
computed:{
...mapState(["counter"])
}
Vue状态管理核心(Vuex)#
最常用的核心概念包含: State
、Getter
、Mutation
、Action
Getter#
对Vuex中的数据进行过滤
import { createStore } from 'vuex'
export default createStore({
state: {
counter: 0
},
getters: {
getCount(state){
return state.counter > 0 ? state.counter : "counter小于0,不符合要求"
}
}
})
import { mapState,mapGetters } from 'vuex';
computed:{
...mapGetters(["getCount"])
}
Mutation#
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的事件类型 (type)和一个回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数
import { createStore } from 'vuex'
export default createStore({
state: {
counter: 0
},
getters: {
},
mutations: {
setCounter(state, num) {
state.counter += num
}
}
})
import { mapState,mapMutations } from 'vuex';
methods:{
...mapMutations(["setCounter"]),
clickHandler(){
// this.$store.commit("setCounter",20)
// 或者
// this.setCounter(10)
}
}
Action#
Action 类似于 mutation,不同在于:
- Action 提交的是 mutation,而不是直接变更状态
- Action 可以包含任意异步操作
import { createStore } from 'vuex'
import axios from "axios"
export default createStore({
state: {
counter: 0
},
getters: {
getCount(state){
return state.counter > 0 ? state.counter : "counter小于0,不符合要求"
}
},
mutations: {
setCounter(state, num) {
state.counter += num
}
},
actions: {
asyncSetCount({ commit }){
axios.get("http://iwenwiki.com/api/generator/list.php")
.then(res =>{
commit("setCounter",res.data[0])
})
}
}
})
import { mapState,mapMutations,mapGetters,mapActions } from 'vuex';
methods:{
...mapActions(["asyncSetCount"]),
clickAsyncHandler(){
// this.$store.dispatch("asyncSetCount")
// 或者
// this.asyncSetCount()
}
}
使用state#
加载
访问
mitaion#
改变存储data
定义:
使用:
getter#
Action#
Action 类似于 mutation,不同在于:
- Action 提交的是 mutation,而不是直接变更状态。
- Action 可以包含任意异步操作。
让我们来注册一个简单的 action:
const store = createStore({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit
提交一个 mutation,或者通过 context.state
和 context.getters
来获取 state 和 getters。当我们在之后介绍到 Modules 时,你就知道 context 对象为什么不是 store 实例本身了。
pinia#
Store 是什么?#
Store (如 Pinia) 是一个保存状态和业务逻辑的实体,它并不与你的组件树绑定。换句话说,它承载着全局状态。它有点像一个永远存在的组件,每个组件都可以读取和写入它。它有三个概念,state、getter 和 action,我们可以假设这些概念相当于组件中的 data
、 computed
和 methods
。
定义 Store#
在深入研究核心概念之前,我们得知道 Store 是用 defineStore()
定义的,它的第一个参数要求是一个独一无二的名字:
import { defineStore } from 'pinia'
// 你可以对 `defineStore()` 的返回值进行任意命名,但最好使用 store 的名字,同时以 `use` 开头且以 `Store` 结尾。(比如 `useUserStore`,`useCartStore`,`useProductStore`)
// 第一个参数是你的应用中 Store 的唯一 ID。
export const useAlertsStore = defineStore('alerts', {
// 其他配置...
})
这个名字 ,也被用作 id ,是必须传入的, Pinia 将用它来连接 store 和 devtools。为了养成习惯性的用法,将返回的函数命名为 use... 是一个符合组合式函数风格的约定。
defineStore()
的第二个参数可接受两类值:Setup 函数或 Option 对象。
Option Store#
与 Vue 的选项式 API 类似,我们也可以传入一个带有 state
、actions
与 getters
属性的 Option 对象
export const useCounterStore = defineStore('counter', {
state: () => ({ count: 0 }),
getters: {
double: (state) => state.count * 2,
},
actions: {
increment() {
this.count++
},
},
})
你可以认为 state
是 store 的数据 (data
),getters
是 store 的计算属性 (computed
),而 actions
则是方法 (methods
)。
为方便上手使用,Option Store 应尽可能直观简单。
Setup Store #
也存在另一种定义 store 的可用语法。与 Vue 组合式 API 的 setup 函数 相似,我们可以传入一个函数,该函数定义了一些响应式属性和方法,并且返回一个带有我们想暴露出去的属性和方法的对象。
js
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
function increment() {
count.value++
}
return { count, increment }
})
在 Setup Store 中:
ref()
就是state
属性computed()
就是getters
function()
就是actions
Setup store 比 Option Store 带来了更多的灵活性,因为你可以在一个 store 内创建侦听器,并自由地使用任何组合式函数。不过,请记住,使用组合式函数会让 SSR 变得更加复杂。
使用 Store #
虽然我们前面定义了一个 store,但在我们使用 <script setup>
调用 useStore()
(或者使用 setup()
函数,像所有的组件那样) 之前,store 实例是不会被创建的:
vue
<script setup>
import { useCounterStore } from '@/stores/counter'
// 可以在组件中的任意位置访问 `store` 变量 ✨
const store = useCounterStore()
</script>
你可以定义任意多的 store,但为了让使用 pinia 的益处最大化(比如允许构建工具自动进行代码分割以及 TypeScript 推断),你应该在不同的文件中去定义 store。
如果你还不会使用 setup
组件,你也可以通过映射辅助函数来使用 Pinia。
State#
在大多数情况下,state 都是你的 store 的核心。人们通常会先定义能代表他们 APP 的 state。在 Pinia 中,state 被定义为一个返回初始状态的函数。这使得 Pinia 可以同时支持服务端和客户端。
js
import { defineStore } from 'pinia'
const useStore = defineStore('storeId', {
// 为了完整类型推理,推荐使用箭头函数
state: () => {
return {
// 所有这些属性都将自动推断出它们的类型
count: 0,
name: 'Eduardo',
isAdmin: true,
items: [],
hasChanged: true,
}
},
})
Getter#
Getter 完全等同于 store 的 state 的计算值。可以通过 defineStore()
中的 getters
属性来定义它们。推荐使用箭头函数,并且它将接收 state
作为第一个参数:
js
export const useStore = defineStore('main', {
state: () => ({
count: 0,
}),
getters: {
doubleCount: (state) => state.count * 2,
},
})
大多数时候,getter 仅依赖 state,不过,有时它们也可能会使用其他 getter。因此,即使在使用常规函数定义 getter 时,我们也可以通过 this
访问到整个 store 实例,但(在 TypeScript 中)必须定义返回类型。这是为了避免 TypeScript 的已知缺陷,不过这不影响用箭头函数定义的 getter,也不会影响不使用 this
的 getter。
Vue3加载Element-plus#
Element,一套为开发者、设计师和产品经理准备的基于 Vue 2.0
的桌面端组件库
Element Plus 基于 Vue 3
,面向设计师和开发者的组件库
安装Element-Plus#
npm install element-plus --save
完整引用#
如果你对打包后的文件大小不是很在乎,那么使用完整导入会更方便
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
const app = createApp(App)
app.use(ElementPlus)
app.mount('#app')
按需导入#
按需导入才是我们的最爱,毕竟在真实的应用场景中并不是每个组件都会用到,这会造成不小的浪费
首先你需要安装unplugin-vue-components
和 unplugin-auto-import
这两款插件
npm install -D unplugin-vue-components unplugin-auto-import
然后修改vue.config.js
配置文件
const { defineConfig } = require('@vue/cli-service')
const AutoImport = require('unplugin-auto-import/webpack')
const Components = require('unplugin-vue-components/webpack')
const { ElementPlusResolver } = require('unplugin-vue-components/resolvers')
module.exports = defineConfig({
transpileDependencies: true,
configureWebpack: {
plugins: [
AutoImport({
resolvers: [ElementPlusResolver()]
}),
Components({
resolvers: [ElementPlusResolver()]
})
]
}
})
最后,可以直接在组件中使用
<template>
<el-button>Default</el-button>
<el-button type="primary">Primary</el-button>
</template>
Vue3加载Element-plus的字体图标#
Element-plus
不仅仅是提供了各种组件,同时还提供了一整套的字体图标方便开发者使用
安装icons
字体图标#
npm install @element-plus/icons-vue
全局注册#
在项目根目录下,创建plugins
文件夹,在文件夹下创建文件icons.js
文件
import * as components from "@element-plus/icons-vue";
export default {
install: (app) => {
for (const key in components) {
const componentConfig = components[key];
app.component(componentConfig.name, componentConfig);
}
},
};
引入文件#
在main.js
中引入icons.js
文件
import elementIcon from "./plugins/icons";
app.use(elementIcon)
使用方式#
接下来就可以直接在组件中引入使用了
<el-icon class="expand" color="#409EFC" :size="30">
<expand />
</el-icon>
作者:Esofar
出处:https://www.cnblogs.com/firsthelloworld/p/17709871.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本