vue ui
或者 vue create project
兼容注意:必须安装Volar插件 ,使用vscode
安装vite
npm init vite@latest
或者
npm init @vitejs/app
//安装vite同时创建vite项目
npm init vite@latest my-vue-app --template vue
//vite.config.ts
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import {loadEnv}from 'vite'
import path from "path";
export default defineConfig({
plugins: [vue()],
base: "./", // 类似publicPath,'./'避免打包访问后空白页面,要加上,不然线上也访问不了
mode:"",//设置开发模式还是生成模式
server: {
https: false, // 是否开启 https
open: false, // 是否自动在浏览器打开
host: "127.0.0.1",//域名
port: 3000, // 端口号
proxy: {
"/api": {
target: "", // 后台接口
changeOrigin: true,//跨域处理
secure: false, // 如果是https接口,需要配置这个参数
// ws: true, //websocket支持
rewrite: (path) => path.replace(/^\/api/, ""),
},
},
resolve: {
alias: {
// 如果报错__dirname找不到,需要安装node,执行npm install @types/node --save-dev
"@": path.resolve(__dirname, "src"),
"@assets": path.resolve(__dirname, "src/assets"),
"@components": path.resolve(__dirname, "src/components"),
"@images": path.resolve(__dirname, "src/assets/images"),
"@views": path.resolve(__dirname, "src/views"),
"@store": path.resolve(__dirname, "src/store"),
},
},
/*
build: {
outDir: "dist",
// 9月更新
assetsDir: "assets", //指定静态资源存放路径
sourcemap: false, //是否构建source map 文件
terserOptions: {
// 生产环境移除console
compress: {
drop_console: true,
drop_debugger: true,
},
},
},
*/
},
// 引入第三方的配置
optimizeDeps: {
//include: ["element-plus/lib/locale/lang/zh-cn"],//举例
},
});
// Vue.config.js 配置选项
module.exports = {
// 选项
// 基本路径
publicPath: "./",
// 构建时的输出目录
outputDir: "dist",
// 放置静态资源的目录
assetsDir: "static",
// html 的输出路径
indexPath: "index.html",
//文件名哈希
filenameHashing: true,
//用于多页配置,默认是 undefined
pages: {
index: {
// page 的入口文件
entry: 'src/index/main.js',
// 模板文件
template: 'public/index.html',
// 在 dist/index.html 的输出文件
filename: 'index.html',
// 当使用页面 title 选项时,
// template 中的 title 标签需要是 <%= htmlWebpackPlugin.options.title %>
title: 'Index Page',
// 在这个页面中包含的块,默认情况下会包含
// 提取出来的通用 chunk 和 vendor chunk。
chunks: ['chunk-vendors', 'chunk-common', 'index']
},
// 当使用只有入口的字符串格式时,
// 模板文件默认是 `public/subpage.html`
// 如果不存在,就回退到 `public/index.html`。
// 输出文件默认是 `subpage.html`。
subpage: 'src/subpage/main.js'
},
// 是否在保存的时候使用 `eslint-loader` 进行检查。
lintOnSave: true,
// 是否使用带有浏览器内编译器的完整构建版本
runtimeCompiler: false,
// babel-loader 默认会跳过 node_modules 依赖。
transpileDependencies: [ /* string or regex */ ],
// 是否为生产环境构建生成 source map?
productionSourceMap: true,
// 设置生成的 HTML 中 和
//引入ref函数
import { ref } from "vue";
setup() {
let name = ref("zs"); //响应式数据 ref()
let job=ref({
type:"前端工程师",
salary:"30k"
})
function sayhi() {
alert("zs");
}
function changeInfo() {
name.value='ls',
job.value.type="java工程师", //修改属性值 需要.value(因为是一个引用实现对象 基本数据类型使用的ref(数据劫持), 对象类型求助了vue内部的reactive函数(proxy)
job.value.salary="40k"
console.log(name);
}
return {
name,
job,
sayhi,
changeInfo,
};
},
import {reactive} from 'vue'
setup(){
// const 对象=reactive({}) 返回一个proxy的代理对象
let person =reactive({ //使用reactive函数可以使对象形式的数据变成响应式
name:'zs',
age:18,
job:{
type:'前端开发',
salary:"30k",
}
})
let hobby=reactive(['抽烟','喝酒','打麻将'])
function changeInfo(){
person.name='ls',
person.age=30,
person.job.type='后端开发',
person.job.salary='40k'
hobby[0]='学习'
}
return {
person, //返回一个代理对象(proxy代理的实例对象)
changeInfo
}
}
//父组件
//具名插槽 #qwe可以写成v-slot:qwe
你好啊哥哥们
//子组件
//具名插槽使用
一个人的信息
姓:
名:
全名:{{person.fullName}}
//与vue2的watch配置功能一致
两个坑:
监听reactive的响应式数据时:oldValue无法正确获取、强制开启了深度监视(deep配置失效)
监听reactive定义的响应式数据中某个属性时:deep配置有效
当前求和为:{{sum}}
当前的信息为:{{msg}}
姓名:{{person.name}}
年龄:{{person.age}}
当前求和为:{{ sum }}
当前的信息为:{{ msg }}
姓名:{{ person.name }}
年龄:{{ person.age }}
薪资:{{ person.job.j1.salary }}K
//两种方式:
第一种与vue2一样,作为配置项来写(beforeDestroy-->beforeUnmount destroyed-->unmounted)
第二种写在setup中(组合式api形式书写)
beforeCreate==>setup()
created==>setup()
其余的钩子全部带on前缀
写法: onMounted(()=>{})
本质上是一个函数,把setup函数中使用得组合式api进行了封装
类似于vue2中得mixin
使用:
当前求和为:{{ sum }}
当前点击时鼠标得坐标为:x:{{point.x}} y:{{point.y}}
//在src下创建hook文件夹 -》创建usePoint.js(封装hooks函数
import { reactive,onMounted,onBeforeUnmount } from "vue"
export default function savePoint() {
//实现鼠标“打点”相关得数据
let point = reactive({
x: 0,
y: 0
})
//实现鼠标“打点”相关得方法
function savePoint(e) {
console.log(e.pageX, e.pageY)
point.x = e.pageX
point.y = e.pageY
}
//实现鼠标“打点”相关得钩子
onMounted(() => {
window.addEventListener('click', savePoint)
})
onBeforeUnmount(() => {
window.removeEventListener('click', savePoint)
})
return point
}
{{name2}}
{{salary}}
姓名:{{ person.name }}
年龄:{{ person.age }}
薪资:{{ person.job.j1.salary }}K
姓名:{{ name }}
年龄:{{ age }}
薪资:{{ job.j1.salary }}K
方式1
import { useRouter } from 'vue-router';
export default {
setup() {
const router = useRouter();
function goto(){
router.push("/about");
}
return{
goto //一定要要放在return里才能在模板上面使用
}
}
}
方式2
import router from "../../router/index.js";
router.push("/");
路由传参
import router from "../../router/index";
router.push({path:'/addShop',query:{id:id}})
//在其他页面获取这个传参
router.currentRoute._rawValue.query.id
import { useStore } from 'vuex'
export default defineComponent({
setup() {
const store = useStore()
const count = store.state.count;
return {
count,
}
}
})
//列表定义的数据
interface Applet {
id?: number;
code: string;
createAt: String,
logo: string,
merchantName: string,
name: string;
serviceName: string;
tag: string;
tagId: string;
}
const appletData = ref<Array<Applet>>([]);
//返回值 data中的值定义
interface AppletListResponse {
count: number;
list: Array<Applet>;
page: number | string;
pageSize: number | string;
}
//列表返回的值定义
interface AppletListRes {
code: number;
msg: string;
data: AppletListResponse;
}
appletList(params).then((res: AppletListRes) => {
if (res.code === 0) {
appletData.value = res.data.list;
total.value = res.data.count;
}
});
//参数
interface AppletParams {
page: number | string;
pageSize: number | string;
name?: string;
storeName?: string;
serviceName?: string;
tag?: string;
}
//接口请求
export const appletList = (params: AppletParams): Promise<AppletListRes> => {
return Http.get("/applet/list", params);
};
shalolowReactive 只考虑对象的第一层数据(浅响应式)
shallowRef 只能处理基本类型数据响应式
//禁止修改数据
readyonly(深只读) shallowReadyonly(浅只读)
{{sum}}
姓名:{{ name }}
年龄:{{ age }}
薪资:{{ job.j1.salary }}K
应用场景:接收别人组件传过来的值,使用readyonly进行保护,操作结果来的值不会影响别人页面
toRaw将reactive生成的响应式数据对象还原成普通对象
使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的操作不会引起页面更新
markRaw:标记一个对象,使其永远不会再成为响应式对象(常用)
应用场景:1.有些之不应被设置为响应式的,例如复杂的第三方类库等
2.当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能
使用:
//祖组件
let car=reactive({
name:'奔驰',
price:'40w'
})
provide('car',car)
//后代组件
let car= inject("car");
return{car}//记得需要return出去
isRef:检查一个值是否为一个ref对象
isReactive:检查一个对象是否由reactive创建的响应式代理
isReadonly:检查一个对象是否由readyonly创建的只读代理
isPoxy:检查一个对象是否由reactive或者readonly方法创建的代理
传送,可以打破多级组件,直接定位到目标结构
// 将该结构直接移动到对应得结构中,比如body,htl等
我是一个弹窗
Suspense :等待异步组件时渲染一些额外的内容,增强用户体验
//异步引入组件
son
//使用Suspense包裹组件,并配置好default和fallback
//在该插槽中使用组件
//组件加载前的结构
加载中
//引入的不再是Vue构造函数了,引入的是一个名为createApp的工厂函数
import {createApp} from 'vue'
import App from './App.vue'
//创建应用实例对象----app(类似于之前的vue2中的vm,但app比vm更'轻'
const app = createApp(App)
//挂载
app.mount('#app')
/*
//vue2挂载方法
const vm= new Vue({
render:h=>h(App)
})
vm.$mount('#app')*/
<html>
...
<body>
<script>
let person={
name:"zs",
age:18
}
//模拟Vue2中实现响应式
let p={}
//正常是需要循环遍历key的
Object.defineProperty(p,'age',{ //数据劫持
configurable:true //可配置的
get(){
return person.age
},
set(value){
console.log('有人修改了age属性,我发现了,我要去更新页面了')
person.age=value
}
})
script>
body>
html>
通过Proxy(代理):拦截对象中任意属性的变化,增删改查
通过Reflect(反射):对(源对象)被代理对象的属性进行操作
<html>
...
<body>
<script>
let person={
name:"zs",
age:18
}
//模拟Vue3中实现响应式 基于Es6的proxy代理
const p=new Proxy(person,{
//有人读取p的某个属性时调用
get(target,propName){
console.log(`有人读取了p身上的${propName}属性`)
//return target[propName]
return Reflect.get(target,propName) //reflect反射对象
},
//有人修改p的某个属性、或给p追加某个属性时调用
set(target,propName,value){
console.log(`有人修改了p身上的${propName}属性,我要去需修改界面了`)
// target[propName]=value
return Reflect.set(target,propName,value)
},
//有人删除p的某个属性时调用
deleteProperty(target,propName){
console.log(`有人删除了p身上的${propName}属性,我要去需修改界面了`)
//return delete taraget[propName]
return Reflect.deleteProperty(target,propName)
}
})
script>
body>
html>
2.x全局api(Vue) 3.x实例api(app)
vue.config.xxx -----> app.config.xxxx
Vue.config.productionTip //vue3中移除
Vue.component -----> app.component
Vue.directive -----> app.directive
Vue.mixin -----> app.mixin
Vue.use -----> app.use
Vue.prototype -----> app.config.globalProperties
1.移除了keyCode作为v-on的修饰符,同时也不再支持config.keyCodes
2.移除了v-on.native修饰符
父组件中绑定事件
子组件中声明自定义事件
3.移除了filter过滤器 (官方推荐使用computed与methods来使用函数处理)
示例:
第一种用computed实现
- 快递公司:{{ item.deliverCompany }}
- 运输状态:{{ computedText(item.expressState) }}
第二种使用methods实现
- 快递公司:{{ item.deliverCompany }}
- 运输状态:{{ methodsText(item.expressState) }}
第一种方案
//在main.js中写入
import { createApp } from 'vue'
import App from './App.vue'
import axios from 'axios'
const app = createApp(App)
app.config.globalProperties.$http = axios//全局挂载
//组件中使用
vue3.0中是没有this的。使用getCurrentInstance来获取上下文
//getCurrentInstance() 获取全局globalProperties 中配置的信息
const { proxy } = getCurrentInstance()// 这里的proxy相当于this
proxy.$http.get('api/getNewsList')
.then((response)=>{
console.log(response)
})
第二种方案
二、使用vue-axios插件
//首先在主入口文件main.js中引用:
import { createApp } from 'vue'
import App from './App.vue'
import axios from 'axios'
import VueAxios from 'vue-axios'
const app = createApp(App)
app.use(VueAxios,axios);
//然后在组件中引用,注意vue3.x没有this
axios.get('api/getNewsList')
.then((response)=>{
console.log(response)
})
安装
npm i mockjs
使用
import Mock from 'mockjs'
示例:
// 使用 Mock
1.直接定义数据
let dataSource = Mock.mock({
'dataSource|5':[{
'key|+1': 1,
'mockTitle|1':['哑巴', 'Butter-fly', '肆无忌惮', '摩天大楼', '初学者'],
'mockContent|1': ['你翻译不了我的声响', '数码宝贝主题曲', '摩天大楼太稀有', '像海浪撞破了山丘'],
'mockAction|1': ['下载', '试听', '喜欢']
}]
})
// 输出结果
console.log(dataSource);
2.模拟接口返回数据:
//第一步:在assets中创建mock.js
import Mock from 'mockjs'
Mock.mock('/meun', /post|get/i, {
// /post|get/i 匹配post和get模式 也可以用'post'或'get'
"ret":0,
"data":
{
"mtime": "@datetime",//随机生成日期时间
"score|1-800": 800,//随机生成1-800的数字
"rank|1-100": 100,//随机生成1-100的数字
"stars|1-5": 5,//随机生成1-5的数字
"nickname": "@cname",//随机生成中文名字
}
})
//第二步在main.js中引入
import './assets/mock'
//第三步使用接口
import axios from 'axios'
axios({
method:'get',
url:'/meun'
}).then((res)=>{
console.log(res);
})
数据模板定义规范:
三部分构成:属性名(name)、生成规则(rule)、属性值(value)
'name|rule':value
根目录下创建vite.config.js
module.exports= {
proxy: {
// 选项写法
'/api': {
target: 'https://pvp.qq.com/', //此处为需要处理跨域的接口
changeOrigin: true, //需要配置
rewrite: (path) => path.replace(/^\/api/, '')//重写地址
},
}
}
//可以直接在模板中使用
{{msg}}
// 可以直接在模板中使用定义的组件
npm i pinia@next
//src/main.ts
import { createApp } from 'vue'
import App from './App.vue'
import { createPinia } from 'pinia'
// 实例化 Vue
const app = createApp(App)
// 安装 Pinia
app.use(createPinia())
// 挂载在真实 DOM
app.mount('#app')
----------------------------------------------------------
//若在vue2中使用 需要额外安装PiniaVuePlugin
import { createPinia, PiniaVuePlugin } from 'pinia'
Vue.use(PiniaVuePlugin)
const pinia = createPinia()
new Vue({
el: '#app',
pinia,
})
//src/store/modules/counter.ts
import {defineStore} from "pinia"
//使用前必须用difineStore()定义一个仓库
//方式一:
export const useCounterStore = defineStore("counter",{
state:()=>{
return {
count:0
}
},
actions:{
increment(){
this.count++
}
}
})
//方式二:使用类似setup()方式定义store
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
function increment() {
count.value++
}
return { count, increment }
})
count:{{counter.count}}