Vue学习笔记
Vue项目实战
小试身手
使用 webpack 搭建 vue 工程实例,结合 ElementUI 组件库快速部署项目
创建
- 安装 webpack:
npm install webpack -g
,npm install webpack-cli -g
- 创建项目:
vue init webpack hello-vue
- 安装路由:
npm install vue-router --save-dev
- 安装 element-ui:
npm i element-ui -S
- 安装 node:
npm install
- 安装 sass 加载器:
npm install sass-loader@7.3.1 --save-dev
npm i -D sass
- 启动测试:
npm run dev
目录
- assets:用于存放资源文件
- components:用于存放Vue功能组件
- views:用于存放Vue视图组件
- router:用于存放vue-router配置
- main.js:入口文件,主要作用是初始化 vue 实例,并引入所需要的插件
- App.vue:主组件,所有页面都是在 App.vue 下进行切换
views
- 创建首页视图,在 views 目录下新建 Main.vue 视图组件
<template>
<div>首页</div>
</template>
<script>
export default {
name:"Main"
}
</script>
<style scoped>
</style>
- 创建登录页视图在views目录下创建名为Login.vue的视图组件(其中el-*的元素为ElementUI组件)
<template>
<div>
<el-form ref="loginForm" :model="form" :rules="rules" label-width="80px" class="login-box">
<h3 class="login-title">欢迎登录</h3>
<el-form-item label="账号" prop="username">
<el-input type="text" placeholder="请输入账号" v-model="form.username"/>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input type="password" placeholder="请输入密码" v-model="form.password"/>
</el-form-item>
<el-form-item>
<el-button type="primary" v-on:click="onsubmit('loginForm')">登录</el-button>
</el-form-item>
</el-form>
<el-dialog title="温馨提示" :visible.sync="dialogVisiable" width="30%" :before-close="handleClose">
<span>请输入账号和密码</span>
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="dialogVisible = false">确定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
export default {
name: "Login",
data(){
return{
form:{
username:'',
password:''
},
//表单验证,需要在 el-form-item 元素中增加prop属性
rules:{
username:[
{required:true,message:"账号不可为空",trigger:"blur"}
],
password:[
{required:true,message:"密码不可为空",tigger:"blur"}
]
},
//对话框显示和隐藏
dialogVisible:false
}
},
methods:{
onSubmit(formName){
//为表单绑定验证功能
this.$refs[formName].validate((valid)=>{
if(valid){
//使用vue-router路由到指定界面,该方式称为编程式导航
this.$router.push('/main');
}else{
this.dialogVisible=true;
return false;
}
});
}
}
}
</script>
<style lang="scss" scoped>
.login-box{
border:1px solid #DCDFE6;
width: 350px;
margin:180px auto;
padding: 35px 35px 15px 35px;
border-radius: 5px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
box-shadow: 0 0 25px #909399;
}
.login-title{
text-align:center;
margin: 0 auto 40px auto;
color: #303133;
}
</style>
route
router目录下存在名为index.js
的vue-router路由配置文件
//导入vue
import Vue from 'vue';
import VueRouter from 'vue-router';
//导入组件
import Main from "../views/Main";
import Login from "../views/Login";
//使用
Vue.use(VueRouter);
//导出
export default new VueRouter({
routes: [
{
//登录页
path: '/main',
component: Main
},
//首页
{
path: '/login',
component: Login
},
]
})
入口
- App.vue
<template>
<div id="app">
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App',
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
- main.js
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from "./router"
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(router)
Vue.use(ElementUI)
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
render:h=>h(App)
})
测试:在浏览器打开 http://localhost:8080/#/login
(如果出现错误: 可能是因为sass-loader的版本过高导致的编译错误,当前最高版本是8.0.2,需要退回到7.3.1 ;在package.json文件里面的 "sass-loader"的版本更换成7.3.1,然后重新cnpm install即可)
进阶
路由嵌套
写两个vue 组件 List 和 Profile,路径分别为/views/user/List
、/views/user/Profile
route.js
...
//导入子模块
import UserList from "../views/user/List";
import UserProfile from "../views/user/Profile";
...
//嵌套路由children
routes: [
{
//登录页
path: '/main',
component: Main,
// 写入子模块
children: [
{
path: '/user/profile',
component: UserProfile,
}, {
path: '/user/list',
component: UserList,
},
]
},
main.vue
...
<el-menu-item index="1-1">
<!--插入的地方-->
<router-link to="/user/profile">个人信息</router-link>
</el-menu-item>
<el-menu-item index="1-2">
<!--插入的地方-->
<router-link to="/user/list">用户列表</router-link>
</el-menu-item>
...
<el-main>
<!--在这里展示视图-->
<router-view />
</el-main>
传参
- 路由增加 props: true 属性,参数为 id
index.js
{
path: '/user/profile/:id',
name:'UserProfile',
component: UserProfile,
props: true
}
- 传递参数,绑定 link
main.vue
<!--name是组件的名字 params是传的参数 如果要传参数的话就需要用v:bind:来绑定-->
<router-link :to="{name:'UserProfile',params:{id:1}}">个人信息</router-link>
- 接收参数,增加 prop 属性
Profile.vue
<template>
<div>
个人信息
{{ id }}
</div>
</template>
<script>
export default {
props: ['id'],
name: "UserProfile"
}
</script>
<style scoped>
</style>
重定向
index.js
export default new Router({
mode: 'history',# 路径去除#号
{
path: '/goHome',
redirect: '/main'
})
说明:这里定义了两个路径,一个是 /main ,一个是 /goHome,其中 /goHome 重定向到了 /main 路径,由此可以看出重定向不需要定义组件
404
NotFound.vue
<template>
<div>
<h1>404,你的页面走丢了</h1>
</div>
</template>
<script>
export default {
name: "NotFound"
}
</script>
<style scoped>
</style>
index.js
import NotFound from '../views/NotFound'
//写了写组件后一定要记得在 index.jx 中导入
{
path: '*',
component: NotFound
}
钩子函数
在 Profile.vue中
export default {
name: "UserProfile",
//在进入路由前执行
beforeRouteEnter: (to, from, next) => {
console.log("准备进入个人信息页");
next();
},
//在进入路由后执行
beforeRouteLeave: (to, from, next) => {
console.log("准备离开个人信息页");
next();
}
}
参数说明:
to:路由将要跳转的路径信息
from:路径跳转前的路径信息
next:路由的控制参数
next() 跳入下一个页面
next(’/path’) 改变路由的跳转方向,使其跳到另一个路由
next(false) 返回原来的页面
next((vm)=>{}) 仅在 beforeRouteEnter 中可用,vm 是组件实例
- 安装 axios
- cnpm install --save vue-axios
- main.js
import axios from 'axios'
import VueAxios from 'vue-axios'
Vue.use(VueAxios, axios)
- Profile.vue
export default {
//第二种取值方式
// props:['id'],
name: "UserProfile",
//钩子函数 过滤器
beforeRouteEnter: (to, from, next) => {
//加载数据
console.log("进入路由之前")
next(vm => {
//进入路由之前执行getData方法
vm.getData()
});
},
beforeRouteLeave: (to, from, next) => {
console.log("离开路由之前")
next();
},
//axios
methods: {
getData: function () {
this.axios({
method: 'get',
url: 'http://localhost:8080/static/mock/data.json'
}).then(function (response) {
console.log(response)
})
}
}
}
Axios
- 安装:npm install axios
- main.js
...
import axios from 'axios'
# 基准路径的配置
axios.default.baseURL = 'http://localhost:8888'
# 挂载到原型对象,别的组件中使用 this.$http
Vue.prototype.$http = axios
Echarts
- 安装:npm install echarts -S
- 也可在 index.html 中直接引入 echarts.js文件(渲染路径:index.html->main.js->App.vue->router/index.js)
- main.js
import * as echarts from "echarts"
# 使用this.$echarts
Vue.prototype.$echarts = window.echarts
- xxx.vue
<template>
<div class="com-container">
<div class="com-chart" ref="sellerRef"></div>
</div>
</template>
<script>
//基本流程:初始化 echart 对象 =>(分辨率监听)=> 获取数据=>(定时器)=> 更新图表(定时器更新)=>离开时销毁定时器及监听器
// import { getSellerData } from '@/api/seller'
export default {
data () {
return {
chartInstance: null,
allData:null //发起请求后得到的数据
}
mounted () {
this.initChart()
this.getData()
// 分辨率适配
window.addEventListener('resize', this.screenAdapter)
// 界面加载完成后主动屏幕适配
this.screenAdapter()
}
destroyed(){
//销毁监听器、定时器等
window.removeEventlisterner('resize', this.screenAdapter)
}
methods: {
// 初始化图表
initChart () {
this.chartInstance = this.$echarts.init(
this.$refs.sellerRef
)
const initOption = {...}
this.chartInstance.setOption(initOption)
}
async getData(){
// http://localhost:8888/api/seller
// axios返回的为 Promise 对象
const (data:ret) = await this.$http.get('seller')
this.allData = ret
this.updataCharts()
}
updataChart(){
// 增量更新
const dataOption = {...}
this.chartInstance.setOption(dataOption)
}
screenAdapter(){
//获取 echart 图表组件宽度
//标题大小
const titleFontSize = this.$refs.seller_ref.offersetWidth/100*3.6
const adapterOption = {
title: {
textStyle: {
fontSize: titleFontSize
}
}
this.chartInstance.setOption(adapterOption)
// 手动的调用图表对象的resize,才能产生效果
this.chartInstance.resize()
}
服务器部署
- config/index.js
build: {
...
assetsPublicPath: '/dist/' //修改为 /var/www/html/ 下的文件夹名称
//即 build 后的文件(index.html、static)所在文件夹
}
- router/index.js
export default new Router({
mode: 'history',
base: '/dist/',//修改这里
routes: [
{
path: '/',
name: 'HelloWorld',
component: HelloWorld
}
]
})
打包:npm run build
生成了 dist 文件夹,将其传输到服务器/var/www/html/下
服务器:
- 安装 apaphe2
- 配置:vim /etc/httpd/conf/httpd.conf
- 修改端口(默认 8080)
- *将所有AllowOverride none 修改为AllowOverride All
后端
Koa
- 项目创建
- 安装:
- 创建 package.json:npm init -y
- npm install koa
- 目录结构:
- 入口文件:app.js
- 数据存放:data/
- 中间件:middleware/
- Koa_response_data.js
- ...
- 工具:utils/
- app.js
// 引入Koa
const Koa = require('koa')
// 创建app
const app = new Koa()
// 应用中间件
// 耗时中间件
app.use(require('./middleware/koa_response_duration'))
// 响应头中间件
app.use(require('./middleware/koa_response_header'))
// 逻辑处理中间件
app.use(require('./middleware/koa_response_data'))
// 监听端口
app.listen(9997)
const webSocketService = require('./service/websocket_service')
// 开启服务端的WebSocket监听
webSocketService.listen()
- koa_response_data.js
const path = require('path')
const getFileData = require('../utils/file_utils')
module.exports = async (ctx, next) => {
let filePath = ctx.request.url.replace('/api', '')
filePath = path.join(__dirname, `../data/${filePath}.json`)
try {
const res = await getFileData(filePath)
ctx.response.body = res
} catch (err) {
const errObj = {
message: '未获取到数据,请检查URL',
status: 404
}
ctx.response.body = errObj
}
await next()
}
跨域:
要满足同源策略(当前页面地址和ajax 发起请求的地址同协议,同域名,同端口),那就需要中间件设置响应头
koa_response_header.js
module.exports = async (ctx, next) => {
await next()
ctx.set('Content-Type', 'application/json;charset=utf-8')
// 允许跨域
ctx.set('Access-Control-Allow-Origin', '*')
ctx.set(
'Access-Control-Allow-Headers',
'Content-Type, Content-Length, Authorization, Accept, X-Requested-With'
)
ctx.set('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS')
}
- 启动:node app.js
WebSocket
后端
- 安装:npm i ws -S
- app.js
const WebSocket = require('ws')
// 创建WebSocket服务端的对象, 绑定的端口号是9998
const wss = new WebSocket.Server({
port: 9998
})
//连接事件监听,client 代表 socket 对象
wss.on('connection',client => {
console.log('连接成功')
// 对 client 对象进行 message 事件监听
client.on('message',msg=>{
console.log(msg)
//向客户端发送数据
client.send("hello,client")
})
})
前端
xxx.html
...
<button id="connect">连接</button>
<button id="send" disabled="true">发送数据</button> <br>
从服务端接收的数据如下: <br>
<span id="recv"></span>
<script>
var connect = document.querySelector('#connect')
var send = document.querySelector('#send')
var recv = document.querySelector('#recv')
let ws = null
connect.onclick = function(){
//连接 ws 服务器
ws = new WebSocket('ws://localhost:9998')
//连接成功事件
ws.onopen = () => {
console.log('连接服务端成功了...')
//button 改为启用状态
send.disabled = false
}
//连接失败事件
ws.onclose = () => {
console.log('连接服务器失败')
send.disabled = true
}
//接收数据事件
ws.onmessage = msg => {
console.log('接收到从服务端发送过来的数据了')
console.log(msg)
recv.innerHTML = msg.data
}
}
send.onclick = function(){
//向服务端发送数据
ws.send(JSON.stringify({
action: 'themeChange',
socketType: 'themeChange',
chartName: '',
value: 'chalk'
}))
}
</script>
技巧
Grid
App.vue
#app{
height: 100%;
margin: 0;
}
view.vue
<script>
beforeCreate () {
document.querySelector('html').setAttribute('style', 'height: 100%;')
document.querySelector('body').setAttribute('style', 'background-color:yellow;height: 100%;')
},
beforeDestroy () {
document.querySelector('html').removeAttribute('style'),
document.querySelector('body').removeAttribute('style')
},
</script>
component.vue
.container{
height: 100%;
margin: 0;
}
文章标题:Vue学习笔记
文章链接:http://120.46.217.131:82/archives/20/
最后编辑:2022 年 9 月 12 日 23:03 By Yang
许可协议: 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)