官网:简介 | Vue.js (vuejs.org)
npm create vue@latest
这一指令将会安装并执行 create-vue,它是 Vue 官方的项目脚手架工具
之后,你将会看到一些诸如 TypeScript 和测试支持之类的可选功能提示:
✔ Project name: …
✔ Add TypeScript? … No / Yes
✔ Add JSX Support? … No / Yes
✔ Add Vue Router for Single Page Application development? … No / Yes
✔ Add Pinia for state management? … No / Yes
✔ Add Vitest for Unit testing? … No / Yes
✔ Add an End-to-End Testing Solution? … No / Cypress / Playwright
✔ Add ESLint for code quality? … No / Yes
✔ Add Prettier for code formatting? … No / Yes
Scaffolding project in ./...
Done.
注意project name 输入的时候不要有大写字母
然后会提示:
Done. Now run:
cd first_vue (刚刚填写的project名称)
npm install
npm run dev
按照要求 进入 first_vue,执行 npm install ,npm run dev
VITE v4.4.9 ready in 494 ms
➜ Local: http://localhost:5173/
➜ Network: use --host to expose
➜ press h to show help
出现以上界面就成功了,在浏览器界面访问 http://localhost:5173/ 可以看到 vue 界面
使用 vscode 进入项目会发现有很多的文件夹
创建好项目了也可以直接在vscode的左下角使用 npm 脚本
下载 volar 插件:
vue3 默认端口5173
想要把端口号修改为9090,在vite.config.js
中写入
server: {
port: 9090,//端口号
host: true,//ip地址 或 '0.0.0.0' 或 "loaclhost"
open: false, //启动后是否自动打开浏览器
https: false, // 是否开启 https
},
执行时间比beforeCreate
还要早
setup函数中,获取不到 this
setup函数中的 数据 和 函数,需要 return,简化请看第4点
如果在 script中 使用setup,就不需要return了(语法糖),帮你return了。
{{ hello }}
例如:
{{ state.count }}
这里不一样的是 既可以接收简单类型,又可以接收对象类型
本质:在原有传入数据的基础上,外层包了一层对象,实际上还是借助了 reactive
注意:在 script
中访问 ref 定义的数据的时候需要通过.value
,但是在template
中 不需要
例如:
{{ count }}
例如:
list:{{ list }}
过滤之后的list:{{ computedList }}
注意:computed中也可以编写 get set方法,可以用来将 ref 对象变为可写 ( ref 对象默认可读)
例如:
作用:侦听一个或者多个数据的变化,数据变化时立即执行回调函数
使用:
例如:
{{ number }}
{{ name }}
这里其实 oldValue 和 oldArr,可以忽略,具体情况具体分析,如果不需要旧值,不接收就好了,这也是newValue等是第一个参数的原因
immediate 和 deep :
immediate:
添加immediate对象,可以让监视在程序加载时立即启动一次,而不需要等待第一次值改变。
例如:
watch(number, (newValue, oldValue) => {
console.log(newValue, oldValue);
},{
immediate:true
})
deep:
deep 是深度监视,watch的默认监视进行的是浅层监视,对于简单类型的值可以直接监视,但监视不到复杂类型内部数据的变化。
例如:
{{ person }}
使用deep相当于监视了对象的所有属性,如果我只需要监听一个特定的属性呢?
也可以实现,例如:
watch(()=>person.value.age, (newValue)=>{
console.log(newValue);
})
就是把第一个参数从对象,换成了一个返回你需要监听属性的函数
每一个生命周期都有一个对应的方法,进行到该生命周期就会触发这个方法。
例如:
onMounted(() => {
console.log("mounted生命周期函数")
})
局部导入:
在components
包下写子组件,例如sonCom.vue
然后在父组件中 import 导入后,直接在中使用标签使用
例如:
父组件
例如,父组件:
父组件
子组件,sonCom:
我是子组件
我是父组件中继承了:{{ car }}, {{ money }}
如果要修改父组件的值:
emit('事件名', 参数)
defineEmits
方法提供 emit 函数,语法const emit = defineEmits(['事件1', '事件2'])
,理解成注册事件就行@emit传来的事件名='xxxx'
,绑定子组件的事件emit
传来的参数例如,父组件:
父组件
当前money:{{ money }}
子组件:
我是子组件
我是父组件中继承了:{{ car }}, {{ money }}
这样也可以实现子传父。
想要使用首先要在vite中修改:
plugins: [
vue({
script: {
defineModel: true, // 启用defineModel
}
}),
],
简介 | Pinia (vuejs.org)
Pinia 是 Vue 的专属状态管理库,它允许你跨组件或页面共享状态。
安装
npm install pinia
导包:
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const pinia = createPinia()
const app = createApp(App)
app.use(pinia)
app.mount('#app')
使用:
defineStore
函数创建对象例如:
testStore:
import { defineStore } from 'pinia'
import { ref } from 'vue';
export const useTestStore = defineStore('testStore', () => {
const count = ref(0);
const subCount = () => {
count.value--;
}
const addCount = () => {
count.value++;
}
return{
count,
subCount,
addCount,
}
})
App.vue:
我是父组件 -- {{ testStore.count }}
sonCom.vue:
我是儿子组件 -- {{ testStore.count }}
我们使用axios来发送异步请求,例如:
import { defineStore } from "pinia";
import axios from 'axios'
import { ref } from "vue";
export const useaxiosStore = defineStore("axiosStore", () => {
const channellist = ref([])
const getList = async() => { //async 表示异步函数
const { data: { data } } = await axios.get("http://localhost:8080/pinia") // 等待数据获取完毕
channellist.value = data.channels
}
return{
channellist,
getList
}
})
app.vue
- {{ i.name }}
java,接收请求并返回数据:
package com.zhx.controller;
import com.zhx.pojo.Result;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.io.PipedReader;
import java.util.*;
@RestController
public class piniaController {
@GetMapping("/pinia")
public Result send(){
class data{
private List<Object> channels;
private String message;
public data(List<Object> channels, String message) {
this.channels = channels;
this.message = message;
}
public List<Object> getChannels() {
return channels;
}
public void setChannels(List<Object> channels) {
this.channels = channels;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
List<Object> list = new ArrayList<>();
for(int i=1;i<=5;i++){
Map<String, Object> map = new HashMap<>();
map.put("id", i);
map.put("name", "name:"+i);
list.add(map);
}
return Result.success(new data(list, "qwq"));
}
}
这里还需要解决一下跨域请求,使用 java 的 config类:
package com.zhx.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
@Configuration
public class CorsConfig {
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
// 允许哪些源访问
config.addAllowedOrigin("http://localhost:5173");
// 允许哪些 HTTP 方法
config.addAllowedMethod("GET");
source.registerCorsConfiguration("/pinia/**", config);
return new CorsFilter(source);
}
}
我们之前都是直接导入仓库,然后实例化,然后使用 .
进行访问,如果想要直接进行解构的话,需要注意两点:
storeToRefs(store)
因为解构会破坏响应性,但是 action ,比如说函数没有响应性可言,就是用就完事了。
例如:
快速开始 | pinia-plugin-persistedstate (prazdevs.github.io)
我们的响应式数据变更之后,如果刷新页面,响应式数据会重置,如果我们需要对响应式数据进行一个持久化,那么就可以使用上面的插件。
安装:npm i pinia-plugin-persistedstate
配置:
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
用法:
创建 Store 时,将 persist
选项设置为 true
。
import { defineStore } from 'pinia'
export const useStore = defineStore(
'main',
() => {
const someState = ref('你好 pinia')
return { someState }
},
{
persist: true, //实际上就是加了一个参数,第三个参数把persist改成true
},
)
还有一些配置操作,可以参考官网:
配置 | pinia-plugin-persistedstate (prazdevs.github.io)
例如,我不希望把整个 state 进行持久化,我们可以使用 path 进行注册
import { defineStore } from 'pinia'
import { ref } from 'vue';
export const useTestStore = defineStore('testStore', () => {
const count = ref(0);
const name = ref('feixin')
const subCount = () => {
count.value--;
}
const addCount = () => {
count.value++;
}
return{
count,
subCount,
addCount,
name,
}
},{
persist: {
paths:['count'] // 这里就只对 count 进行了持久化,而name没有
}
})
指令 | 作用 |
---|---|
v-bind | 为HTML标签绑定属性值,如设置href,css样式等, v-bind:href 可以省略为 :href |
v-model | 在表单元素上创建双向数据绑定 |
v-on | 为HTML标签绑定事件 |
v-if | 类下 |
v-else-if | 条件性的渲染某元素,判定为true时渲染,否则不渲染 |
v-else | 类上 |
v-show | 根据条件展示某元素,区别在于切换的是display属性的值 |
v-for | 列表渲染,遍历容器的元素或者对象的属性 |
v-bind 和 v-model
:
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>vue testtitle>
<script src="../js/vue.js">script>
head>
<body>
<div id="app">
<a v-bind:href="url">这是一个链接a>
<input type="text" v-model="url">
div>
body>
<script>
new Vue({
el: "#app",
data:{
url:"https://www.baidu.com"
}
})
script>
html>
可以实现更改表单中的网址,旁边的a标签绑定的网址也会发生相应的变化
注意:通过 v-bind 或者 v-model 绑定的变量,必须在数据模型中声明。
v-on
:
可以绑定很多时间比如click,blur,focus
, 与 js
事件类似
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>vue testtitle>
<script src="../js/vue.js">script>
head>
<body>
<div id="app">
<input type="button" value="click me" v-on:click="handle()">
div>
body>
<script>
new Vue({
el: "#app",
data: {
url: "https://www.baidu.com"
},
methods: {
handle: function () {
alert("i have been clicked");
}
},
})
script>
html>
效果就是点击后触发事件
条件指令 v-if v-else-if v-else v-show
:
if,else-if,else
是一起配套使用的
例如:
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>vue testtitle>
<script src="../js/vue.js">script>
head>
<body>
<div id="app">
年龄:<input type="text" v-model="age">经判定,为:
<span v-if="age<35">年轻人 35岁以下span>
<span v-else-if="age>=35 && age<=60">中年人 35-60span>
<span v-else>老年人 60以上span>
div>
body>
<script>
new Vue({
el: "#app",
data: {
age:20,
},
})
script>
html>
效果为修改输入框中的值,后面的判定字会改变,if 这一套 如果 条件为 else 的话标签直接不会在浏览器中被渲染,也就是根本不会出现在浏览器源码当中。
v-show
:
上述效果同样可以通过v-show
来实现。
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>vue testtitle>
<script src="../js/vue.js">script>
head>
<body>
<div id="app">
年龄:<input type="text" v-model="age">经判定,为:
<span v-if="age<35">年轻人 35岁以下span>
<span v-else-if="age>=35 && age<=60">中年人 35-60span>
<span v-else>老年人 60以上span>
<br><br>
年龄:<input type="text" v-model="age">经判定,为:
<span v-show="age<35">年轻人 35岁以下span>
<span v-show="age>=35 && age<=60">中年人 35-60span>
<span v-show="age>60">老年人 60以上span>
div>
body>
<script>
new Vue({
el: "#app",
data: {
age:20,
},
})
script>
html>
效果是一样的,区别在于v-show
如果条件为 false 的话标签依然会被浏览器渲染,不过是被display:none
设置了不展示
v-for
: 遍历数据对象
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>vue testtitle>
<script src="../js/vue.js">script>
head>
<body>
<div id="app">
<div v-for="addr in addrs" :key="index">
{{addr}}
div>
<hr>
<div v-for="(addr, index) in addrs" :key="index">
{{index + 1}} : {{addr}}
div>
div>
body>
<script>
new Vue({
el: "#app",
data: {
addrs:["beijing","shanghai","xian","guilin"]
},
})
script>
html>
安装 | Element Plus (element-plus.org)
根据文档,在当前目录下安装
或者使用以下方式:
vue3+vite2增加element-plus的CDN链接打包构建 - 掘金 (juejin.cn)
按需引入:
pnpm element-plus
pnpm add -D unplugin-vue-components unplugin-auto-import
在vite.config.js中配置:
import AutoImport from 'unplugin-auto-import/vite' //---1
import Components from 'unplugin-vue-components/vite' //---2
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'//---3
export default defineConfig({
plugins: [
vue(),
AutoImport({
resolvers: [ElementPlusResolver()] //---1
}),
Components({
resolvers: [ElementPlusResolver()] //---2
})
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
})
创建如下目录
在ElementView.vue
中编写
Default
Primary
Success
Info
Warning
Danger
在 App.vue
中编写
即可导入。
组件 | Element
进去复制粘贴使用,懂?
引入组件之后需要在components
中声明一下才可以使用
例如:
<script>
import { Menu as IconMenu, Message, Setting } from '@element-plus/icons-vue';
// 这里引入了组件
export default {
data() {
return {
searchForm: {
name: "",
gender: "",
entryDate: [],
}
}
},
components: {
Message, Setting
//这里需要声明
},
methods: {
query() {
alert(JSON.stringify(this.searchForm));
},
sizeChange(val){
alert("每页记录数变化" + val)
},
currentChange(val){
alert("页码发生变化" + val)
},
}
}
script>
npm install -g pnpm
参考旁边的文件 大事件管理系统中的 ESlint配置
+
别打上去
pnpm dlx husky-init && pnpm install
们会使用 axios 来请求后端接口, 一般都会对 axios 进行一些配置 (比如: 配置基础地址等)
一般项目开发中, 都会对 axios 进行基本的二次封装, 单独封装到一个模块中, 便于使用
pnpm add axios
新建 utils/request.js
封装 axios 模块
利用 axios.create 创建一个自定义的 axios 来使用
http://www.axios-js.com/zh-cn/docs/#axios-create-config
import axios from 'axios'
const baseURL = 'http://big-event-vue-api-t.itheima.net'
const instance = axios.create({
// TODO 1. 基础地址,超时时间
})
instance.interceptors.request.use(
(config) => {
// TODO 2. 携带token
return config
},
(err) => Promise.reject(err)
)
instance.interceptors.response.use(
(res) => {
// TODO 3. 处理业务失败
// TODO 4. 摘取核心响应数据
return res
},
(err) => {
// TODO 5. 处理401错误
return Promise.reject(err)
}
)
export default instance
import { useUserStore } from '@/stores/user'
import axios from 'axios'
import router from '@/router'
import { ElMessage } from 'element-plus'
const baseURL = 'http://big-event-vue-api-t.itheima.net'
const instance = axios.create({
baseURL,
timeout: 100000
})
instance.interceptors.request.use(
(config) => {
const userStore = useUserStore()
if (userStore.token) {
config.headers.Authorization = userStore.token
}
return config
},
(err) => Promise.reject(err)
)
instance.interceptors.response.use(
(res) => {
if (res.data.code === 0) {
return res
}
ElMessage({ message: res.data.message || '服务异常', type: 'error' })
return Promise.reject(res.data)
},
(err) => {
ElMessage({ message: err.response.data.message || '服务异常', type: 'error' })
console.log(err)
if (err.response?.status === 401) {
router.push('/login')
}
return Promise.reject(err)
}
)
export default instance
export { baseURL }
npm run build
可以进行打包,打包之后会出现在dist中