# 前置知识

export 导出, import 导入, as 取别名
export 可以添加在方法前,也可以统一导出,通过 default 默认导出

  • export {complexMessage as cm}
  • export default {complexMessage}
  • import {complexMessage as cm} from './test.js'
  • import methods from './test.js'

vscode 插件:Live Server
test.js

s
function simpleMessage(msg) {
    console.log(msg);
}
// 方法一:函数前添加 export
export function complexMessage(msg) {
  console.log(new Date() + " - " + msg);
}
// 方法二:统一 export
export {simpleMessage, complexMessage as cm};
// 方法三:默认 export
export default {simpleMessage, complexMessage};

test.html

l
<!DOCTYPE html>
<html lang="zh">
  <head>
    <meta charset="UTF-8">
    <title>测试</title>
</html>
</head>
<body>
  <div>
    <button id="message">测试</button>
  </div>
  <script type="module">
    // 方法一
    import {simpleMessage as sm, cm} from './test.js';
    document.getElementById('message').onclick = function() {
      cm('测试,测试,测试');
    }
    // 方法二
    import methods from './test.js';
    document.getElementById('message').onclick = function() {
        methods.complexMessage('测试,测试,测试');
    }
  </script>
</body>
</html>

# 框架介绍

Vue:用于构建用户界面的渐进式 js 框架

l
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>Vue测试</title>
</html>
</head>
<body>
    <div id="app"></div>
    <!-- ES 格式,引入 Vue 模块 -->
    <script type="module">
      import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'
      
      /* 创建实例 */
      createApp({
        data() {
          return {
            // Vue 实例数据
            message: 'Hello Vue!'
          }
        }
      }).mount('#app')  // 控制 id 为 app 的元素
    </script>
</body>
</html>

# 常用指令

  • v-for:列表渲染,遍历元素; v-for = "(item,index) in items"
  • v-bind:为 html 标签绑定属性值; v-bind:href:href
  • v-if/v-else-if/v-else:条件判断,控制元素的创建和移除; v-if="表达式"
  • v-show:切换 display 属性值,控制元素的显示与隐藏; v-show="表达式"
  • v-on:为 html 标签绑定事件; v-on:click="方法名"@:click="方法名"
  • v-model:创建双向数据绑定,获取和设置数据; v-model="变量名"
l
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>Vue测试</title>
</head>
<body>
    <div id="app">
        <table border="1" cellspacing="0" cellpadding="5">
        <tr>
            <th>标题</th>
            <th>内容</th>
            <th>作者</th>
        </tr>
        <tr v-for="article in articleList">
            <!-- v-for 指令用于循环遍历 articles 数组 -->
            <td></td>
            <td></td>
            <td></td>
        </tr>
        </table>
        
        <!-- v-bind 指令用于绑定属性 -->
        <a v-bind:href="url" target="_blank">点击跳转1 </a>
        <a :href="url" target="_blank">点击跳转2</a>
        <!-- v-if 指令用于条件渲染 -->
        <div v-if="articleList.length > 0">
            <p>文章列表不为空</p>
        </div>
        <div v-else>
            <p>文章列表为空</p> 
        </div>
        <!-- v-show 指令用于显示或隐藏元素 -->
        <div v-show="articleList.length != 0">
            <p>文章列表不为空</p>
        </div>
        <!-- v-on 指令用于绑定事件 -->
        <div>
            <button v-on:click="complexMessage('按钮被点击了1')">点击我1</button>
            <button @click="complexMessage('按钮被点击了2')">点击我2</button>
        </div>
        <!-- v-model 指令用于双向数据绑定 -->
        <div>
            <input type="text" v-model="name" placeholder="请输入姓名" />
            <p>姓名:</p>
            <button @click="clear">清空</button>
        </div>
    </div>
    <!-- ES 格式,引入 Vue 模块 -->
    <script type="module">
      import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'
      
      /* 创建实例 */
      createApp({
        data() {
          return {
            // Vue 实例数据
            articleList: [
              { title: 'Vue.js', content: 'Vue.js是一个渐进式JavaScript框架', author: '尤雨溪' },
              { title: 'React', content: 'React是一个用于构建用户界面的JavaScript库', author: 'Facebook' },
              { title: 'Angular', content: 'Angular是一个用于构建单页应用的框架', author: 'Google' }
            ],
            url: 'https://www.example.com',
            name: ''
          }
        },
        methods: {
            // Vue 实例方法
            complexMessage: function(msg) {
                console.log(new Date() + ' ' + msg)
                alert(msg)
            },
            clear: function() {
                this.name = ''
            }
        }
      }).mount('#app')  // 挂载到 id 为 app 的元素上,控制元素
    </script>
</body>
</html>

# 生命周期

生命周期:一个对象从创建到销毁的整个过程
八个阶段,会自动执行一个生命周期方法(钩子)
Vue生命周期

状态阶段周期
beforeCreate创建前
created创建后
beforeMount载入前
mounted挂载完成
beforeUpdate数据更新前
updated数据更新后
beforeUnmount组件销毁前
unmounted组件销毁后
s
createApp({
    mounted: function() {
        // Vue 实例挂载
        console.log('Vue实例已挂载')
    }
}).mount('#app')

# Axios

Ajax(Asynchronous Javascript And XML),即是异步的 JavaScript 和 XML
Axios 对原生的 Ajax 进行封装

# 使用

l
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>Axios测试</title>
</head>
<body>
<!-- 引入 axios -->
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"> </script>
<script>
    // 发送 GET 请求
    axios({
        method: 'GET',
        url: 'https://api.github.com/users/octocat',
    }).then((resp) => {
        // 成功回调
        console.log(resp);
    }).catch((err) => {
        // 失败回调
        console.error(err);
    });
    // 发送 POST 请求
    axios({
        method: 'POST',
        url: 'https://jsonplaceholder.typicode.com/posts',
        data: {
            title: 'foo',
            body: 'bar',
            userId: 1,
        },
    }).then((resp) => {
        // 成功回调
        console.log(resp);
    }).catch((err) => {
        // 失败回调
        console.error(err);
    });
    // 别名方式发送请求
    axios.get('https://api.github.com/users/octocat')
        .then((resp) => {
            // 成功回调
            console.log(resp);
        }).catch((err) => {
            // 失败回调
            console.error(err);
        });
    axios.post('https://jsonplaceholder.typicode.com/posts', {
        title: 'foo',
        body: 'bar',
        userId: 1,
    }).then((resp) => {
        // 成功回调
        console.log(resp);
    }).catch((err) => {
        // 失败回调
        console.error(err);
    });
</script>
</body>
</html>

# 拦截器

s
import axios from "axios";
const baseurl = "http://www.example.com"
const instance = axios.create({baseurl});  // 创建 axios 实例
// 添加请求拦截器
instance.interceptors.request.use(function (config) {
    // 在发送请求之前做些什么
    // 可以添加请求头
    return config;
  }, function (error) {
    // 对请求错误做些什么
    return Promise.reject(error);
  });
// 添加响应拦截器
instance.interceptors.response.use(function (response) {
    // 2xx 范围内的状态码都会触发该函数。
    if (response.data.code === 0) {
      // 请求成功
      return response.data;
    }else {
      // 请求失败
      console.log(response.data.msg);
      return Promise.reject(response.data.msg);
    }
  }, function (error) {
    // 超出 2xx 范围的状态码都会触发该函数。
    // 对响应错误做点什么
    return Promise.reject(error);
  });
export default instance;

# 项目创建

h
# 方法一
npm init vue@latest
npm install
npm run dev
# 方法二
pnpm create vue@latest
pnpm install
pnpm dev  # 运行
pnpm build  # 打包

项目结构:

  • package.json:项目配置文件
  • vite.config.js:Vue 项目的配置信息
  • src
    • assets
    • components:组件目录
    • App.vue:根组件
    • main.js:入口文件

文件关系
*.vue:单文件组件,封装逻辑(script)、模板(template)和样式(style)

l
<!-- 方法一 -->
<script>
  export default {
    data() {
      return {
        title: 'Hello Vue 3!'
      }
    }
  }
</script>
<!-- 方法二 -->
<script setup>
  import { ref } from 'vue'
  const title = ref('Welcome!')
</script>
<template>
  <h1>Vue</h1>
</template>
<style scoped>
h1 {
  color: #42b983;
  font-size: 2em;
  text-align: center;
}
</style>

# API 风格

选项式 API:用 datamethodsmounted

l
<script>
  export default {
    data() {
      return {
        title: 'Hello Vue 3!'
      }
    }
  }
</script>

组合式 API: ref 响应式数据
一般会将方法封装到 js 文件中,export 出来,函数复用
导入自定义方法: import {test} from '@/api/myapi.js'@ 表示 src 目录

l
<!-- 方法二 -->
<script setup>
// pnpm add axios
  import axios from 'axios'
  import { onMounted, ref } from 'vue'
  const title = ref('Welcome!')
  const data = ref({
    name: 'Vue 3',
    role: "admin"
  })
  function changeTitle() {
    //... 解构运算符,将对象的属性展开
    axios.get('https://www.example.com', {params: {...data.value}})
      .then(response => {
        title.value = 'success!'
      })
      .catch(error => {
        title.value = 'fail!'
      })
  }
  onMounted(() => {
    setTimeout(() => {
      changeTitle()
    }, 2000)
  })
</script>

# Element Plus

Element Plus:基于 Vue 3,面向设计师和开发者的组件库
Overview 组件总览
安装: pnpm install element-plus
完整导入
main.js

s
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import zhCn from 'element-plus/es/locale/lang/zh-cn'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import App from './App.vue'
const app = createApp(App)
app.use(ElementPlus, {
  locale: zhCn,
})
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
  app.component(key, component)
}
app.mount('#app')

调用其他.vue

s
<template>
  <demo></demo>
</template>
<script setup>
import demo from "@/views/demo.vue";
</script>

# 跨域

由于浏览器的同源策略策略,向不同源(不同协议、不同域名、不同端口)发送 ajax 请求会失败
vite.config.js 配置 proxy 代理,前端向 /api 请求,会向 8080 端口请求,将 api 替换为空

s
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueDevTools from 'vite-plugin-vue-devtools'
// https://vite.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    vueDevTools(),
  ],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    },
  },
  server: {
    proxy: {
      '/api': {
        target: 'http://localhost:3000',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, '')
      }
    }
  }
})

# 路由

路由:决定从起点到终点的路径的进程,根据不同访问路径,显示不同组件内容
官方路由:Vue Router
安装: pnpm add vue-router
router 实例:路由实例,createRouter 创建,维护路由信息
<router-link> :路由链接组件,解析为 <a>
<router-view> :动态视图组件,渲染与路由路径对应的组件
路由跳转:router.push ('/')

src/router/index.js
创建路由器并导出

s
import { createRouter, createWebHistory } from "vue-router";
// 导入组件
import LoginVue from "@/views/Login.vue";
import LayoutVue from "@/views/Layout.vue";
import UserInfoVue from "@/views/UserInfo.vue";
import UserSettingVue from "@/views/UserSetting.vue";
// 定义路由
const routes = [
    { path: '/login', component: LoginVue },
    { 
        path: '/', 
        component: LayoutVue,
        // 子路由
        children: [
            {path: '/user/info', component: UserInfoVue},
            {path: '/user/setting', component: UserSettingVue},
        ]
    }
]
// 创建路由器
const router = createRouter({
    history: createWebHistory(),
    routes: routes
});
export default router;

main.js

s
import router from '@/router'
app.use(router)

# Pinia

Vue 的专属状态管理库,允许跨组件和页面共享状态,传递 token 等信息
pnpm add pinia
main.js

s
import { createPinia } from 'pinia'
const pinia = createPinia()
app.use(pinia)

src/stores/token.js
定义 store 方法

s
import { defineStore } from "pinia";
import { ref } from "vue";
export const useTokenStore = defineStore("token", () => {
  const token = ref("");
  function setToken(newToken) {
    token.value = newToken;
  }
  function clearToken() {
    token.value = "";
  }
  return { token, setToken, clearToken };
}
);

之后在组件中引入 @/stores/token.js ,调用 useTokenStore
pinia 默认存储在内存中,刷新浏览器时会丢失数据
Persist 插件:将 pinia 内的数据持久化存储
pnpm add pinia-persistedstate-plugin
main.js

s
import { createPersistedState } from 'pinia-persistedstate-plugin'
const persistedState = createPersistedState()
pinia.use(persistedState)

创建 store 时添加 persist 选项
export const useTokenStore = defineStore("token", ()=>{}, {persist: true});

# Demo

s
<template>
  <el-container class="layout-container-demo">
    <el-header style="text-align: center; font-size: 20px">
      tlias管理系统
    </el-header>
    <el-container>
      <!-- 侧边栏 -->
      <el-aside width="140px">
        <el-menu>
          <el-sub-menu index="1">
            <template #title>
              <el-icon><Menu /></el-icon>部门管理
            </template>
            <el-menu-item index="1-1">Option 1-1</el-menu-item>
            <el-menu-item index="1-2">Option 1-2</el-menu-item>
            <el-menu-item index="1-3">Option 1-3</el-menu-item>
          </el-sub-menu>
          <el-sub-menu index="2">
            <template #title>
              <el-icon><Avatar /></el-icon>员工管理
            </template>
            <el-menu-item index="2-1">Option 1-1</el-menu-item>
            <el-menu-item index="2-2">Option 1-2</el-menu-item>
            <el-menu-item index="2-3">Option 1-3</el-menu-item>
          </el-sub-menu>
          <el-sub-menu index="3">
            <template #title>
              <el-icon><Search /></el-icon>数据统计
            </template>
            <el-menu-item index="3-1">Option 1-1</el-menu-item>
            <el-menu-item index="3-2">Option 1-2</el-menu-item>
            <el-menu-item index="3-3">Option 1-3</el-menu-item>
          </el-sub-menu>
        </el-menu>
      </el-aside>
      <el-container>
        <el-main>
          <!-- 对话框 -->
          <el-button
            plain
            @click="dialogTableVisible = true"
            style="margin-bottom: 20px"
          >
            点击查看数据
          </el-button>
          <el-dialog v-model="dialogTableVisible" title="数据" width="600">
            <el-table :data="emps">
              <el-table-column property="id" label="ID" width="150" />
              <el-table-column property="name" label="姓名" width="200" />
              <el-table-column property="deptName" label="部门" />
            </el-table>
          </el-dialog>
          <!-- 表单 -->
          <el-form :inline="true" :model="formInline">
            <el-form-item label="用户名">
              <el-input
                style="width: 150px"
                v-model="formInline.name"
                clearable
              />
            </el-form-item>
            <el-form-item label="性别">
              <el-select
                style="width: 100px"
                v-model="formInline.gender"
                clearable
              >
                <el-option label="所有" value="0" />
                <el-option label="男" value="1" />
                <el-option label="女" value="2" />
              </el-select>
            </el-form-item>
            <el-form-item label="开始时间">
              <el-date-picker
                style="width: 150px"
                v-model="formInline.begin"
                type="date"
                value-format="YYYY-MM-DD"
                clearable
              />
            </el-form-item>
            <el-form-item label="结束时间">
              <el-date-picker
                style="width: 150px"
                v-model="formInline.end"
                type="date"
                value-format="YYYY-MM-DD"
                clearable
              />
            </el-form-item>
            <el-form-item>
              <el-button type="primary" @click="search">查询</el-button>
              <el-button type="info" @click="clear">清空</el-button>
            </el-form-item>
          </el-form>
          <!-- 表格数据 -->
          <el-table :data="emps">
            <el-table-column prop="id" label="ID" align="center"/>
            <el-table-column prop="username" label="用户名" align="center" />
            <el-table-column prop="name" label="姓名" align="center"/>
            <el-table-column prop="gender" label="性别" width="60px" align="center">
                <template #default="scope">
                    <!--swig6-->
                </template>
            </el-table-column>
            <el-table-column prop="phone" label="电话号码" align="center" />
            <el-table-column prop="job" label="职位" align="center" >
                <template #default="scope">
                    <span v-if="scope.row.job == 1">班主任</span>
                    <span v-else-if="scope.row.job == 2">讲师</span>
                    <span v-else-if="scope.row.job == 3">学工主管</span>
                    <span v-else-if="scope.row.job == 4">教研主管</span>
                    <span v-else-if="scope.row.job == 5">咨询师</span>
                    <span v-else>其他</span>
                </template>
            </el-table-column>
            <el-table-column prop="salary" label="薪水" align="center" />
            <el-table-column prop="entryDate" label="入职日期" align="center" />
            <el-table-column prop="deptName" label="部门" align="center" />
            <el-table-column prop="updateTime" label="修改时间" width="200px" align="center" />
          </el-table>
          <!-- 分页栏 -->
          <div class="demo-pagination-block">
            <el-pagination
              v-model:current-page="currentPage"
              v-model:page-size="pageSize"
              :page-sizes="[5, 10, 50, 100]"
              :background="true"
              layout="sizes, prev, pager, next"
              v-model:total="total"
              @size-change="search"
              @current-change="search"
            />
          </div>
        </el-main>
      </el-container>
    </el-container>
  </el-container>
</template>
<script lang="ts" setup>
import { ref, reactive, onMounted } from "vue";
import axios from "axios";
const currentPage = ref(1);
const pageSize = ref(5);
const total = ref(50);
const dialogTableVisible = ref(false);
const formInline = ref({
  name: "",
  gender: "0",
  begin: "",
  end: "",
});
const emps = ref([]);
const search = async () => {
  const data = await axios.get("http://localhost:8080/emps", {
    params: {
      page: currentPage.value,
      pageSize: pageSize.value,
      ...formInline.value,
    },
  });
  emps.value = data.data.data.rows;
  total.value = data.data.data.total;
};
const clear = () => {
    emps.value = [];
}
onMounted(() => {
  search();
});
</script>
<style scoped>
</style>
更新于