使用Vue制作的项目合集
MVVM模式
M (Model,模型层 )
V (View,视图层)
VM(ViewModel,V与M连接的桥梁,也可以看作为控制器)
1、 M:模型层,主要负责业务数据相关;
2、 V:视图层,顾名思义,负责视图相关,细分下来就是html+css层;
3、 VM:V与M沟通的桥梁,负责监听M或者V的修改,是实现MVVM双向绑定的要点;
MVVM支持双向绑定,意思就是当M层数据进行修改时,VM层会监测到变化,并且通知V层进行相应的修改,
反之修改V层则会通知M层数据进行修改,以此也实现了视图与模型层的相互解耦
安装node很方便,只需要一条命令 可以轻松切换node版本 可以多版本node并存
nvm install 8.11.1 #下载8.11.1
nvm use 8.9.0 #使用8.9.0
nvm alias default 6.10.0 #设置默认版本
配置vite.config.js文件
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
server:{
port:3000 // 配置访问接口 http://localhost:3000/
}
})
利用Vite构建工具来创建项目
# npm 6.x
npm init vite@latest <project-name> --template vue
# npm 7+,需要加上额外的双短横线
npm init vite@latest <project-name> -- --template vue
cd <project-name>
npm install
npm run dev
// 安装脚手架
`npm install -g @vue/cli`
// 更新脚手架
`npm update -g @vue/cli`
选项式API (Vue2)
使用选项式API,我们可以用包含多个选项的对象来描述组件的逻辑,例如 data、methods 和 mounted
选项所定义的属性都会暴露在函数内部的 this 上,它会指向当前的组件实例
代码结构
<template>
</template>
<script>
export default {
// 数据结构
data (){return {}},
// 方法
methods: {},
//计算属性 计算属性与数据结构同级,都是用来表示数据,但是计算属性可以加上逻辑处理
computed: {},
//侦听器 可以监控数据变化前后的值
watch:{},
//生命周期钩子
mounted() {},
}
</script>
组合式API (Vue3)
组合式API是使用导入的方式来描述组件逻辑,通常会与 <script setup> 搭配使用
<script setup> 里面的代码会被编译成组件 setup() 函数的内容
<script setup>
import {ref, computed, watch, onMounted} from 'vue'
// 创建响应式数据
let username = ref('')
// 事件绑定
function ClickMe()
{
alert('触发事件')
}
// 计算属性 计算属性与数据结构同级,都是用来表示数据,但是计算属性可以加上逻辑处理
let count = computed(() => {
var num = 150.417
return num.toFixed(2)
})
//侦听器 可以监控数据变化前后的值
watch(username, (newVal, oldVal) => {
console.log(newVal, oldVal)
})
// 获取组件中ref="hello"的真实dom元素
const hello = ref(null)
// 生命周期钩子
onMounted(() => {
console.log(hello.value) // <input type="text">
console.log(hello.value.value) // 张三
})
</script>
<template>
<input type="text" v-model="username" />
{{username}}
<br />
<button @click="ClickMe">绑定事件-简写方式</button>
<h1>计算属性</h1>
{{count}}
<h1>Ref用法 获取DOM节点</h1>
<input type="text" value="张三" ref="hello" />
</template>
Vue2 | Vue3 | 说明 |
---|---|---|
创建实例前/后 | ||
beforeCreate | setup | 创建实例前 vue实例的挂载元素el和数据对象data都为undefined,还未初始化,此时拿不到数据 场景:可以加loading……事件 ,加载实列时触发 |
created | setup | 创建实例后 实例已经创建,仍然不能获取DOM节点(有data,没有el)此时data 和 methods已经可以使用 但是页面还没有渲染出来 场景:一些异步请求的调用 ,loading……事件结束等 |
载入数据前/后 | ||
beforeMount | onBeforeMount | 载入前 vue挂载的根节点已经创建(有data,有el)实例尚未挂载完成 ,此时页面上还看不到真实数据 |
mounted | onMounted | 载入后 数据和DOM已被渲染 场景:在这个钩子函数里面可以使用一些第三方的插件 |
更新 前/后 | ||
beforeUpdate | onBeforeUpdate | 更新前 数据更新时调用 发生在虚拟DOM打补丁之前。 页面上数据还是旧的 |
updated | onUpdated | 更新后 更新结束后执行 页面上数据已经替换成最新的 场景:做一些数据统一更新处理的相应函数 |
销毁 前/后 | ||
beforeDestroy | onBeforeUnmount | 销毁前 在实例销毁之前调用 ,在这一步,实例仍然完全可用 |
destroyed | onUnmounted | 销毁后 实例销毁后调用。该钩子被调用后,对应 Vue 实例的所有指令都被解绑,所有的事件监听器被移除,所有的子实例也都被销毁。 |
缓存 | ||
activated | onActivated | 组件激活时 / 停用时 这两个钩子需要配合配合<keep-alive><keep-alive/>来使用 keep-alive的作用会缓存不活动的组件实例,而不是销毁它们。当组件在<keep-alive>内被切换,activated和deactivated这两个生命周期钩子函数将会被对应执行 场景:做一些footer底部菜单、tab的切换激活 |
deactivated | onDeactivated | |
错误处理机制 | ||
errorCaptured | onErrorCaptured | 当这个钩子检测到组件中发生错误时就被调用。 通过err, vm, info这3个参数输出: err 错误对象 vm 发生错误的组件实例 info 包含错误来源信息的字符串,比如错误所在的生命周期钩子 此钩子可以返回 false 以阻止该错误继续向上传播 场景:能快速找到报错的组件位置,还能解决满屏红等视觉冲击 |
Vue3新增(仅在开发模式下可用的调试钩子) | ||
----- | onErrorCaptured | 每次渲染后重新收集响应式依赖,在onMounted前触发,页面更新后也会触发 |
----- | onRenderTriggered | 每次触发页面重新渲染时的自动执行,在onBeforeUpdate之前触发 |
Vue3 中新增的钩子,它是我们在编写组件时的一个入口API
同时也是 Vue3 中新增的一个生命周期函数,会在 beforeCreate 之前调用
因为此时组件的 data 和 methods 还没有初始化,因此在 setup 中是不能使用 this 的
所以 Vue 为了避免我们错误的使用,它直接将 setup 函数中的 this 修改成了undefined
并且,我们只能同步使用setup函数,不能用async将其设为异步
setup 函数接收两个参数 props 和 context, 语法为:setup(props,context){}
props
props 里面包含父组件传递给子组件的所有数据。在子组件中使用 props 进行接收
props 是响应式的, 当传入新的 props 时,会及时被更新
由于是响应式的, 所以不可以使用 ES6 解构,解构会消除它的响应式
context
context 里面包含 attrs, slots, emit 等数据方法
attrs:获取组件上的属性
slots:获取 slot 插槽的节点
emit :emit 方法(子组件向父组件传递数据)
选项式API (Vue2)
<template>
<input type="text" v-model="username" />
<h1>{{username}}</h1>
</template>
<script>
import {ref} from 'vue'
export default {
setup(props, context)
{
//获取传递的属性
console.log(props)
// 获取传递的自定义属性(非响应式的对象,等价于 $attrs)
console.log(context.attrs)
// 插槽(非响应式的对象,等价于 $slots)
console.log(context.slots)
// 触发事件(函数,等价于 $emit)
console.log(context.emit)
// 暴露公共属性(函数)
console.log(context.expose)
let username = ref('hello world')
return {
username
}
}
}
</script>
组合式API (Vue3)
<script setup>
import {ref} from 'vue'
let username = ref('hello world')
// defineProps() 定义组件传参属性方法
// defineEmits() 定义组件传参事件方法
// defineExpose() 暴露当前组件的数据和方法
// useSlots() 获取插槽对象
// useAttrs() 获取自定义属性对象
</script>
<template>
<input type="text" v-model="username" />
<h1>{{username}}</h1>
</template>
<script setup>
// 引入响应式数据
import { ref, reactive, toRef, toRefs, readonly } from 'vue'
// --------- ref(简单) ---------
// 创建单个响应式数据
let username = ref('张三')
// 修改单个响应式数据
setTimeout(() => {
username.value = '李四'
}, 3000)
// --------- reactive(复杂) ---------
// 创建复杂类型响应式数据
let info = reactive({
name: '张三',
age: 20,
sex: '男'
})
// 修改复杂类型响应式数据
setTimeout(() => {
info.name = '李四'
info.age = 30
info.sex = '女'
}, 3000)
// --------- toRef(从复杂中抽离单个) ---------
// 单个的抽离
let name = toRef(info, 'name')
// --------- toRefs(从复杂类型中抽离多个) ---------
// 抽离多个元素 抽离的元素 必须是 info 对象中有的属性
let {sex ,age} = toRefs(info)
// --------- readonly(只读) ---------
//设置只读属性
let readname = readonly(username)
</script>
<template></template>
在Vue2中,修改某一些数据,视图是不能及时重新渲染的
原因就是 Vue2 中的数据响应式是利用 object.definedProperty()实现的,它是无法深层监听数据的变化的
而Vue3,利用的是ES6的proxy,对数据响应式进行一个数据的代理
如果想要让一个对象变为响应式数据,可以使用reactive或ref
因此Vue3也就把 set方法 废了
Vue2中使用前需要再data中现将需要使用的数据定义出来
data:{
user:{
name:"sally",
age:31,
gender:"male",
salary:"secret"
},
list:[1,2,3,4]
}
对象使用:
(1)全局方法:Vue.set(user, 'address', 'beijing')(对象名,键名,键值)
(2)实例方法:vm.$set(user, 'address', 'beijing')
数组使用:
(1)全局方法:Vue.set(list, 2, 10)(数组名,下标,新的数据)
(2)实例方法:vm.$set(list, 3, 8)
可以归为两大类: 全局通信(父传子 父:main.js)
局部通信(组件之间) 父传子(props属性) 子传父(event事件) 由于Vue2和Vue3编写方式不同,每种通信又有两种写法
props和context
1、props(父传子)
props 是组件的自定义属性
父组件通过 props 向子组件传递要展示的数据
2、context(子传父)
是一个 Javascript 对象,它公开了三个组件属性
context 里面包含 attrs, slots, emit 等数据方法
attrs:获取组件上的属性(非响应式对象)
slots:获取 slot 插槽的节点
emit :emit 方法(子组件向父组件传递数据)
父组件
<template>
<!-- 4、通过v-bin绑定自定义属性( :属性名 简写) -->
<Son1 :name="nametext"></Son1>
</template>
<script>
import { ref } from 'vue'
<!-- 1、引入子组件 -->
import Son1 from './son1.vue'
export default {
<!-- 3、使用setup()钩子设置需要传递的数据 -->
setup ()
{
const nametext = ref('张三')
return nametext
},
<!-- 2、注册子组件 -->
components: {
Son1
}
}
</script>
子组件 把动态的数据项声明为 props 自定义属性
自定义属性可以在当前组件的模板结构中被直接使用
<template>
<!-- 3、直接使用 -->
<div>{{name}}</div>
</template>
<script>
import { toRef} from 'vue'
export default {
<!-- 1、声明props自定义属性 -->
<!-- 特别说明:此处name是父组件中子组件标签上的属性名,不是参数名 -->
props:
{
name: String,
},
<!-- 2、通过setup()接收数据 -->
setup(props)
{
console.log(props.name)
console.log(name.value, age.value)
}
}
</script>
父组件
<script setup>
import {ref} from 'vue'
import Son1 from './son1.vue'
let text = ref('张三')
</script>
<template>
<!-- 父组件向子组件传递数据 -->
<Son1 :name="text"></Son1>
</template>
子组件
<script setup>
//接收props参数
const props = defineProps({
name: String,
})
console.log(props)
</script>
<template>
{{props.name}}
</template>
defineProps() 定义组件属性API
defineEmits() 定义组件执行器API
defineExpose() 暴露当前组件的数据和方法
useSlots() 获取插槽对象
useAttrs() 获取自定义属性对象
父组件
<template>
<Son2 @SubClick="SubClick">parent</Son2>
</template>
<script>
import { ref } from 'vue'
import Son2 from './son2.vue'
export default {
setup ()
{
function SubClick (data)
{
// 接收子组件传递过来的数据
console.log(data)
}
return SubClick
},
components: {
Son2,
}
}
</script>
子组件
<template>
<!-- 父组件向子组件传递数据 -->
<button @click="HandleClick">Children</button>
</template>
<script>
export default {
setup (props, context)
{
let { attrs, slots, emit } = context
<!-- // vue3.x 获取组件上的属性
console.log(attrs.SubData)
// vue3.x 获取slot插槽的节点
console.log(slots.default()) -->
// vue3.x emit方法(子组件向父组件传递数据)
function HandleClick ()
{
emit('SubClick', 'vue3.x - this is subData')
}
return HandleClick
}
}
</script>
父组件
<script setup>
import {ref} from 'vue'
import Son2 from './son2.vue'
//自定义事件
function SubClick(data)
{
//接收子组件传递过来的数据
console.log(data)
}
</script>
<template>
<!-- 子组件向父组件传递数据 -->
<Son2 SubData="some other data" @SubClick="SubClick"></Son2>
</template>
子组件
<script setup>
import { useSlots, useAttrs } from 'vue'
// useSlots() 获取插槽对象
// useAttrs() 获取自定义属性对象
const attrs = useAttrs()
const slots = useSlots()
// 获取组件上的属性
console.log(attrs)
// 获取slot插槽的节点
console.log(slots)
// emit方法(子组件向父组件传递数据)
const emit = defineEmits(['SubClick'])
function HandleClick()
{
emit('SubClick', 'vue3.x - this is subData')
}
</script>
<template>
<!-- 子组件向父组件传递数据 -->
<button @click="HandleClick">子组件传递数据</button>
</template>
父传子 provide - 可以向所有子组件提供数据以及提供修改数据的方法 inject - 子组件用inject来接收数据和方法 可以用来设置Vue全局属性和方法
父组件
<template>
<h1>Vue3新特性 - provide 与 inject (父组件向子组件传递数据与方法)</h1>
<Son3></Son3>
</template>
<script>
import { ref, provide } from 'vue'
import Son3 from './son3.vue'
export default {
setup()
{
const name = ref('张三')
// provide(别名, 要传递的数据和方法)
provide('myName', name)
provide('HandleClick', () => {
name.value = 'zhangsan'
})
},
components: { Son3 }
}
</script>
子组件
<template>
<button @click="HandleClick"> {{ name }} 点击一下我</button>
</template>
<script>
import { inject } from 'vue'
export default {
setup()
{
const name = inject('myName')
const HandleClick = inject('HandleClick')
return {
name,
HandleClick
}
}
}
</script>
src/main.js
const test = () => {
return '全局方法'
}
const app = createApp(App)
.mount('#app')
//全局依赖注入
app.provide('$test', test)
组件中使用
<script setup>
import { inject } from 'vue'
//调用注入的全局方法
const test = inject('$test');
console.log(test())
</script>
父组件
<template>
<h1>Vue3新特性 - 插槽新写法</h1>
<Son4>
<!-- Vue2.x写法
<div slot="parent">
<div>父组件</div>
</div>
-->
<template v-slot:parent>
<div>父组件</div>
</template>
</Son4>
</template>
<script>
import Son4 from './son4.vue'
export default {
components:{
Son4
}
}
</script>
子组件 son4.vue
<template>
<slot name='parent'>子组件</slot>
</template>
父组件
<template>
<h1>Vue3新特性 - 作用域插槽</h1>
<Son5>
<!-- <template slot="content" slot-scope="scoped"> -->
<template v-slot:content="scoped">
<div>{{scoped.myName}}</div>
</template>
</Son5>
</template>
<script>
import Son5 from './son5.vue'
export default {
components: {
Son5
}
}
</script>
子组件 son5.vue
<template>
<slot name="content" :myName="myName"></slot>
</template>
<script>
import { ref } from 'vue'
export default {
setup ()
{
let myName = ref("hello world")
return { myName }
},
}
</script>
Vue-Router 官网
安装路由
npm install vue-router@4 -S
src/router.js
//引入路由对象
import { createRouter, createWebHashHistory} from "vue-router"
//引入组件
import home from './components/home.vue'
import about from './components/about.vue'
// 创建路由
export default createRouter({
history: createWebHashHistory(), //hash路由模式
linkExactActiveClass: 'active', //点击路由跳转的底部样式标量
//路由列表
routes: [
{
//默认首页
path: '/',
name: 'home',
component: home
},
{
path: '/about',
name: 'about',
component: about
},
]
})
src/main.js
import router from './router'
createApp(App)
.use(router)
.mount('#app')
src/App.vue
<template>
<router-view></router-view>
<h1>路由构建</h1>
<router-link to="/">去首页</router-link><br />
<router-link to="/about">关于我们</router-link><br />
</template>
逻辑结构
src/router.js
//引入路由对象
import { createRouter, createWebHashHistory} from "vue-router"
//引入组件
import list from './components/list.vue'
import info from './components/info.vue'
// 创建路由
export default createRouter({
history: createWebHashHistory(), //hash路由模式
linkExactActiveClass: 'active', //点击路由跳转的底部样式标量
//路由列表
routes: [
{
path: '/list',
name: 'list',
component: list
},
{
path: '/info/:id',
name: 'info',
component: info
},
]
})
src/App.vue
<template>
<router-view></router-view>
<h1>路由操作</h1>
<p>
接收 query 参数的时候 需要使用 path属性<br />
接收 params 参数的时候 需要使用 name属性<br />
</p>
<!-- 路由跳转组件 -->
<router-link to="/list">跳转到列表路由(无参数)</router-link><br />
<router-link :to="{path:'/list', query:{keyword:123}}">跳转到列表路由(有参数)</router-link><br />
<router-link :to="{name:'info', params:{id:100}}">跳转到详细页面路由</router-link><br />
</template>
components/list.vue
<template>
<h1>列表页面</h1>
接收query参数 {{$route.query}}
</template>
components/info.vue
<template>
<h1>详细界面</h1>
接收params参数 {{$route.params}}
</template>
由于项目前端中可能会分成好几大不同模块,每个模块下又有许多界面,为了方便管理,可以将路由按照模块进行划分
// 项目路由结构
localhost:3000 一级路由 二级路由
/ -> 首页
/business -> 用户相关
/login -> 用户登录界面
/register -> 用户注册界面
由此可以将 /routers 文件夹下的index.js中的路由信息,通过变量获取来配置
实现逻辑思路(以shop项目为例)
1、先划分出主要模块,在routers路由文件夹下创建一级路由js文件,目录如下:
2、以business.js模块为例,其文件内部与普通路由文件没有太多区别,只需要注意组件单独定义部分,文件如下:
3、将模块划分配置完成后后便可修改入口index.js文件
4、将路由对象 createRouter 中的 routes 配置项的配置值使用 RouterList 自定义变量控制( 存放路由列表 )
5、引入当前目录下的所有js文件并使用 modfile 自定义变量存放( /routers文件夹下的js文件存放的都是路由文件 )
6、modfile 为数组结构,其中key值为一级路由地址 value值为二级路由地址,具体数据格式如下:
7、通过使用Object.value()方法抽离出value,生成新的数组,再利用map方法、同异步操作,将value添加到RouterList路由集合变量中使用
//index.js
//引入路由对象
import { createRouter, createWebHashHistory } from 'vue-router'
//引入当前目录下面所有的JS文件
const modfile = import.meta.globEager('./*.js')
//路由集合
const RouterList = []
// console.log(Object.keys(modfile)) //提取对象中所有的key 放到一个数组中
// console.log(Object.values(modfile)) //提取对象中所有的value 放到一个数组中
Object.values(modfile).map(async mod => {
if(mod.default) await RouterList.push(mod.default)
})
//创建路由对象
export default createRouter({
history: createWebHashHistory(), //设置路由模式
linkExactActiveClass: 'active', //当前所匹配的链接
routes: RouterList, //路由列表
})
抽离公共自定义组件及第三方插件(自动引入组件)
安装自动导入组件的插件
vue3+vite插件配置系列3-unplugin-vue-components(官方文档翻译)
npm install unplugin-vue-components -S
unplugin-auto-import的使用
npm install unplugin-auto-import -S
安装插件后,需要在vite.config.js中进行相关配置
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
//引入读取路径的一个nodejs模块
import path from 'path'
// 引入自动导入组件插件
import Components from 'unplugin-vue-components/vite'
// 自动导入vue,项目插件库
import AutoImport from 'unplugin-auto-import/vite'
// https://vitejs.dev/config/
export default defineConfig({
// 别名配置
resolve: {
alias: {
//设置路径的别名
// __dirname 当前所在的目录 /shop
// path.resolve 拼接路径 /shop/src
"@": path.resolve(__dirname, "src"),
'vue': 'vue/dist/vue.esm-bundler.js' // 定义vue的别名,如果使用其他的插件,可能会用到别名
}
},
// 访问端口设置
server: {
port: 3000
},
plugins: [
vue(),
//设置自动加载规则
Components({
// 指定组件位置,默认是src/components
dirs: ['src/components'],
extensions: ['vue'],
}),
//自动导入库
AutoImport({
imports: ['vue', 'vue-router']
})
]
})
在后台fastAdmin中,新建一个shop模块、business控制器文件夹、business控制器文件
在控制器文件中编写接口信息以及处理数据
路由文件:routers/business.js 页面组件:components/business/register.vue
接口地址:www.fast.com/shop/business/register 控制器文件地址:application/shop/Controller/Business.php -> function register
api接口文件模板:application/api/controller/Demo.php
// Business.php (API接口文件控制器)
<?php
namespace app\shop\controller;
use think\Controller;
// 引入API接口文件
use app\common\controller\Api;
/**
* 用户接口
*/
// 继承Api
class Business extends Api
{
public function __construct()
{
parent::__construct();
$this->BusinessModel = model('Business.Business');
}
// 此处注解需要参考命令行>一键生成API文档
/**
* 用户注册
*
* @ApiTitle (用户注册)
* @ApiSummary (用户注册)
* @ApiMethod (POST)
* @ApiParams (name="mobile", type="string", required=true, description="手机号")
* @ApiParams (name="password", type="string", required=true, description="密码")
* @ApiReturnParams (name="code", type="integer", required=true, sample="0")
* @ApiReturnParams (name="msg", type="string", required=true, sample="返回成功")
* @ApiReturnParams (name="data", type="object", sample="{'user_id':'int','user_name':'string','profile':{'email':'string','age':'integer'}}", description="扩展数据返回")
* @ApiReturn ({
'code':'1',
'msg':'返回成功'
})
*/
// 具体的接口方法(reigster注册接口)
public function reigster()
{
if($this->request->isAjax())
{
$mobile = $this->request->param('mobile', '', 'trim');
$password = $this->request->param('password', '', 'trim');
var_dump($mobile, $password);
}
}
}
有许多命令行操作,参考文档:
知音必答_命令行
fastadmin_命令大全
一键生成API文档
FastAdmin中的一键生成API文档可以在命令行或后台一键生成我们API接口的接口测试文档,可以直接在线模拟接口请求,查看参数示例和返回示例
常用命令
//一键生成API文档
php think api --force=true
//指定https://www.example.com为API接口请求域名,默认为空
php think api -u https://www.example.com --force=true
//输出自定义文件为myapi.html,默认为api.html
php think api -o myapi.html --force=true
//修改API模板为mytemplate.html,默认为index.html
php think api -e mytemplate.html --force=true
//修改标题为FastAdmin,作者为作者
php think api -t FastAdmin -a Karson --force=true
//查看API接口命令行帮助
php think api -h
参数介绍
-u, --url[=URL] 默认API请求URL地址 [default: ""]
-m, --module[=MODULE] 模块名(admin/index/api) [default: "api"]
-o, --output[=OUTPUT] 输出文件 [default: "api.html"]
-e, --template[=TEMPLATE] 模板文件 [default: "index.html"]
-f, --force[=FORCE] 覆盖模式 [default: false]
-t, --title[=TITLE] 文档标题 [default: "FastAdmin"]
-a, --author[=AUTHOR] 文档作者 [default: "FastAdmin"]
-c, --class[=CLASS] 扩展类 (multiple values allowed)
-l, --language[=LANGUAGE] 语言 [default: "zh-cn"]
注释规则
在我们的控制器中通常分为两部分注释,一是控制器头部的注释,二是控制器方法的注释
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。