<html>
<head>
<title>$emit 和 $on</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="root">
<button @click="boost">触发事件</button>
</div>
<script>
new Vue({
el: '#root',
data() {
return {
message: 'hello vue'
}
},
created() {
this.$on('my_events', this.handleEvents)
},
methods: {
handleEvents(e) {
console.log(this.message, e)
},
boost() {
this.$emit('my_events', 'my params')
}
}
})
</script>
</body>
</html>
<html>
<head>
<title>directive 用法</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="root">
<div v-loading="isLoading">{{data}}</div>
<button @click="update">更新</button>
</div>
<script>
Vue.directive('loading', {
update(el, binding, vnode) {
if (binding.value) {
const div = document.createElement('div')
div.innerText = '加载中...'
div.setAttribute('id', 'loading')
div.style.position = 'absolute'
div.style.left = 0
div.style.top = 0
div.style.width = '100%'
div.style.height = '100%'
div.style.display = 'flex'
div.style.justifyContent = 'center'
div.style.alignItems = 'center'
div.style.color = 'white'
div.style.background = 'rgba(0, 0, 0, .7)'
document.body.append(div)
} else {
document.body.removeChild(document.getElementById('loading'))
}
}
})
new Vue({
el: '#root',
data() {
return {
isLoading: false,
data: ''
}
},
methods: {
update() {
this.isLoading = true
setTimeout(() => {
this.data = '用户数据'
this.isLoading = false
}, 3000)
}
}
})
</script>
</body>
</html>
<html>
<head>
<title>Vue.component 用法</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="root">
<Test :msg="message"></Test>
</div>
<script>
Vue.component('Test', {
template: '<div>{{msg}}</div>',
props: {
msg: {
type: String,
default: 'default message'
}
}
})
new Vue({
el: '#root',
data() {
return {
message: "Test Component"
}
}
})
</script>
</body>
</html>
<html>
<head>
<title>Vue.extend 用法</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="root">
<Test :msg="message"></Test>
</div>
<script>
const component = Vue.extend({
template: '<div>{{msg}}</div>',
props: {
msg: {
type: String,
default: 'default message'
}
},
name: 'Test'
})
Vue.component('Test')
new Vue({
el: '#root',
data() {
return {
message: "Test Extend Component"
}
}
})
</script>
</body>
</html>
<html>
<head>
<title>Vue.extend 用法2</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<style>
#loading-wrapper {
position: fixed;
top: 0;
left: 0;
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
background: rgba(0,0,0,.7);
color: #fff;
}
</style>
</head>
<body>
<div id="root">
<button @click="showLoading">显示Loading</button>
</div>
<script>
function Loading(msg) {
const LoadingComponent = Vue.extend({
template: '<div id="loading-wrapper">{{msg}}</div>',
props: {
msg: {
type: String,
default: msg
}
},
name: 'LoadingComponent'
})
const div = document.createElement('div')
div.setAttribute('id', 'loading-wrapper')
document.body.append(div)
new LoadingComponent().$mount('#loading-wrapper')
return () => {
document.body.removeChild(document.getElementById('loading-wrapper'))
}
}
Vue.prototype.$loading = Loading
new Vue({
el: '#root',
methods: {
showLoading() {
const hide = this.$loading('正在加载,请稍等...')
setTimeout(() => {
hide()
}, 2000)
}
}
})
</script>
</body>
</html>
<html>
<head>
<title>Vue.use 用法</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<style>
#loading-wrapper {
position: fixed;
top: 0;
left: 0;
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
background: rgba(0,0,0,.7);
color: #fff;
}
</style>
</head>
<body>
<div id="root">
<button @click="showLoading">显示Loading</button>
</div>
<script>
const loadingPlugin = {
install: function(vm) {
const LoadingComponent = vm.extend({
template: '<div id="loading-wrapper">{{msg}}</div>',
props: {
msg: {
type: String,
default: 'loading...'
}
}
}, 'LoadingComponent')
function Loading(msg) {
const div = document.createElement('div')
div.setAttribute('id', 'loading-wrapper')
document.body.append(div)
new LoadingComponent({
props: {
msg: {
type: String,
default: msg
}
}
}).$mount('#loading-wrapper')
return () => {
document.body.removeChild(document.getElementById('loading-wrapper'))
}
}
vm.prototype.$loading = Loading
}
}
Vue.use(loadingPlugin)
new Vue({
el: '#root',
methods: {
showLoading() {
const hide = this.$loading('正在加载,请稍等...')
setTimeout(() => {
hide()
}, 2000)
}
}
})
</script>
</body>
</html>
<html>
<head>
<title>组件通信 provide 和 inject</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="root">
<Test></Test>
</div>
<script>
function registerPlugin() {
Vue.component('Test', {
template: '<div>{{message}}<Test2 /></div>',
provide() {
return {
elTest: this
}
}, // function 的用途是为了获取运行时环境,否则 this 将指向 window
data() {
return {
message: 'message from Test'
}
},
methods: {
change(component) {
this.message = 'message from ' + component
}
}
})
Vue.component('Test2', {
template: '<Test3 />'
})
Vue.component('Test3', {
template: '<button @click="changeMessage">change</button>',
inject: ['elTest'],
methods: {
changeMessage() {
this.elTest.change(this.$options._componentTag)
}
}
})
}
Vue.use(registerPlugin)
new Vue({
el: '#root'
})
</script>
</body>
</html>
<html>
<head>
<title>过滤器 filter</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="root">
{{message | lower}}
</div>
<script>
new Vue({
el: '#root',
filters: {
lower(value) {
return value.toLowerCase()
}
},
data() {
return {
message: 'Hello Vue'
}
}
})
</script>
</body>
</html>
<html>
<head>
<title>监听器 watch</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="root">
<h3>Watch 用法1:常见用法</h3>
<input v-model="message">
<span>{{copyMessage}}</span>
</div>
<div id="root2">
<h3>Watch 用法2:绑定方法</h3>
<input v-model="message">
<span>{{copyMessage}}</span>
</div>
<div id="root3">
<h3>Watch 用法3:deep + handler</h3>
<input v-model="deepMessage.a.b">
<span>{{copyMessage}}</span>
</div>
<div id="root4">
<h3>Watch 用法4:immediate</h3>
<input v-model="message">
<span>{{copyMessage}}</span>
</div>
<div id="root5">
<h3>Watch 用法5:绑定多个 handler</h3>
<input v-model="message">
<span>{{copyMessage}}</span>
</div>
<div id="root6">
<h3>Watch 用法6:监听对象属性</h3>
<input v-model="deepMessage.a.b">
<span>{{copyMessage}}</span>
</div>
<script>
new Vue({
el: '#root',
watch: {
message(value) {
this.copyMessage = value
}
},
data() {
return {
message: 'Hello Vue',
copyMessage: ''
}
}
})
new Vue({
el: '#root2',
watch: {
message: 'handleMessage'
},
data() {
return {
message: 'Hello Vue',
copyMessage: ''
}
},
methods: {
handleMessage(value) {
this.copyMessage = value
}
}
})
new Vue({
el: '#root3',
watch: {
deepMessage: {
handler: 'handleDeepMessage',
deep: true
}
},
data() {
return {
deepMessage: {
a: {
b: 'Deep Message'
}
},
copyMessage: ''
}
},
methods: {
handleDeepMessage(value) {
this.copyMessage = value.a.b
}
}
})
new Vue({
el: '#root4',
watch: {
message: {
handler: 'handleMessage',
immediate: true,
}
},
data() {
return {
message: 'Hello Vue',
copyMessage: ''
}
},
methods: {
handleMessage(value) {
this.copyMessage = value
}
}
}),
new Vue({
el: '#root5',
watch: {
message: [{
handler: 'handleMessage',
},
'handleMessage2',
function(value) {
this.copyMessage = this.copyMessage + '...'
}]
},
data() {
return {
message: 'Hello Vue',
copyMessage: ''
}
},
methods: {
handleMessage(value) {
this.copyMessage = value
},
handleMessage2(value) {
this.copyMessage = this.copyMessage + '*'
}
}
})
new Vue({
el: '#root6',
watch: {
'deepMessage.a.b': 'handleMessage'
},
data() {
return {
deepMessage: { a: { b: 'Hello Vue' } },
copyMessage: ''
}
},
methods: {
handleMessage(value) {
this.copyMessage = value
}
}
})
</script>
</body>
</html>
<html>
<head>
<title>class 和 style 绑定的高级用法</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="root">
<div :class="['active', 'normal']">数组绑定多个class</div>
<div :class="[{active: isActive}, 'normal']">数组包含对象绑定class</div>
<div :class="[showWarning(), 'normal']">数组包含方法绑定class</div>
<div :style="[warning, bold]">数组绑定多个style</div>
<div :style="[warning, mix()]">数组包含方法绑定style</div>
<div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }">style多重值</div>
</div>
<script>
new Vue({
el: '#root',
data() {
return {
isActive: true,
warning: {
color: 'orange'
},
bold: {
fontWeight: 'bold'
}
}
},
methods: {
showWarning() {
return 'warning'
},
mix() {
return {
...this.bold,
fontSize: 20
}
}
}
})
</script>
</body>
</html>
<html>
<head>
<title>Vue.observable</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="root">
{{message}}
<button @click="change">Change</button>
</div>
<script>
const state = Vue.observable({ message: 'Vue 2.6' })
const mutation = {
setMessage(value) {
state.message = value
}
}
new Vue({
el: '#root',
computed: {
message() {
return state.message
}
},
methods: {
change() {
mutation.setMessage('Vue 3.0')
}
}
})
</script>
</body>
</html>
<html>
<head>
<title>插槽 slot</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="root">
<div>案例1:slot的基本用法</div>
<Test>
<template v-slot:header="{user}">
<div>自定义header({{user.a}})</div>
</template>
<template v-slot="{user}">
<div>自定义body({{user.b}})</div>
</template>
</Test>
</div>
<div id="root2">
<div>案例2:Vue2.6新特性 - 动态slot</div>
<Test>
<template v-slot:[section]="{section}">
<div>this is {{section}}</div>
</template>
</Test>
<button @click="change">switch header and body</button>
</div>
<script>
Vue.component('Test', {
template:
'<div>' +
'<slot name="header" :user="obj" :section="'header'">' +
'<div>默认header</div>' +
'</slot>' +
'<slot :user="obj" :section="'body'">默认body</slot>' +
'</div>',
data() {
return {
obj: { a: 1, b: 2 }
}
}
})
new Vue({ el: '#root' })
new Vue({
el: '#root2',
data() {
return {
section: 'header'
}
},
methods: {
change() {
this.section === 'header' ?
this.section = 'default' :
this.section = 'header'
}
}
})
</script>
</body>
</html>
vue create element-test
vuex+router ([Vue 2] router, vuex, dart-sass, babel, eslint)
Default ([Vue 2] babel, eslint)
Default (Vue 3) ([Vue 3] babel, eslint)
Manually select features
image.png
vue create element-test
2 . 安装
npm i element-ui -S
3 . Vue 插件
import ElementUI from 'element-ui'
Vue.use(ElementUI)
4 . 引用样式
import 'element-ui/lib/theme-chalk/index.css'
Entrypoints:
app (1.03 MiB)
css/chunk-vendors.a16c4353.css
js/chunk-vendors.29976e71.js
css/app.601d2ada.css
js/app.29e7d705.js
For more info visit https://webpack.js.org/guides/code-splitting/
File Size Gzipped
dist\js\chunk-vendors.29976e71.js 845.63 KiB 225.63 KiB
dist\js\app.29e7d705.js 2.77 KiB 1.30 KiB
dist\css\chunk-vendors.a16c4353.css 209.55 KiB 33.63 KiB
dist\css\app.601d2ada.css 0.25 KiB 0.19 KiB
Images and other types of assets omitted.
按需加载的用法如下:
npm install babel-plugin-component -D
2 . 修改 babel.config.js:
{
"presets": [["es2015", { "modules": false }]],
"plugins": [
[
"component",
{
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
}
]
]
}
Vuex 的原理关键:使用 Vue 实例管理状态
<html>
<head>
<title>vuex 原理解析</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="root">{{data}}</div>
<div id="root2">{{data2}}</div>
<div id="root3">
<button @click="change">change</button>
</div>
<script>
function registerPlugin(Vue) {
const vuex = {}
vuex._vm = new Vue({
data: {
message: 'hello vue.js'
}
})
vuex.state = vuex._vm
vuex.mutations = {
setMessage(value) {
vuex.state.message = value
}
}
function init() {
this.$store = vuex
}
Vue.mixin({
beforeCreate: init
})
}
Vue.use(registerPlugin)
new Vue({
el: '#root',
computed: {
data() {
return this.$store.state.message
}
}
})
new Vue({
el: '#root2',
computed: {
data2() {
return this.$store.state.message
}
}
})
new Vue({
el: '#root3',
methods: {
change() {
const newValue = this.$store.state.message + '.'
this.$store.mutations.setMessage(newValue)
}
}
})
</script>
</body>
</html>
vue-router 实例化时会初始化 this.history,不同 mode 对应不同的 history
constructor (options: RouterOptions = {}) {
this.mode = mode
switch (mode) {
case 'history':
this.history = new HTML5History(this, options.base)
break
case 'hash':
this.history = new HashHistory(this, options.base, this.fallback)
break
case 'abstract':
this.history = new AbstractHistory(this, options.base)
break
default:
if (process.env.NODE_ENV !== 'production') {
assert(false, `invalid mode: ${mode}`)
}
}
}
这里以 HashHistory 为例,vue-router 的 push 方法实现如下:
push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
// $flow-disable-line
if (!onComplete && !onAbort && typeof Promise !== 'undefined') {
return new Promise((resolve, reject) => {
this.history.push(location, resolve, reject)
})
} else {
this.history.push(location, onComplete, onAbort)
}
}
HashHistory 具体实现了 push 方法:
function pushHash (path) {
if (supportsPushState) {
pushState(getUrl(path))
} else {
window.location.hash = path
}
}
对路由的监听通过 hash 相应的事件监听实现:
window.addEventListener(
supportsPushState ? 'popstate' : 'hashchange',
() => {
const current = this.current
if (!ensureSlash()) {
return
}
this.transitionTo(getHash(), route => {
if (supportsScroll) {
handleScroll(this.router, route, current, true)
}
if (!supportsPushState) {
replaceHash(route.fullPath)
}
})
}
)
除此之外,vue-router 还提供了两个组件:
Vue.component('RouterView', View)
Vue.component('RouterLink', Link)
创建 router.js:
import Vue from 'vue'
import Route from 'vue-router'
import HelloWorld from './components/HelloWorld'
Vue.use(Route)
const routes = [
{ path: '/hello-world', component: HelloWorld }
]
const router = new Route({
routes
})
export default router
在 main.js 中引用 router,并加入 vue 实例:
import router from './router'
new Vue({
render: h => h(App),
router
}).$mount('#app')
router.beforeEach((to, from, next) => {
console.log('beforeEach', to, from)
next()
})
router.beforeResolve((to, from, next) => {
console.log('beforeResolve', to, from)
next()
})
router.afterEach((to, from) => {
console.log('afterEach', to, from)
})
beforeRouteEnter (to, from, next) {
// 不能获取组件实例 `this`
console.log('beforeRouteEnter', to, from)
next()
},
beforeRouteUpdate (to, from, next) {
console.log('beforeRouteUpdate', to, from)
next()
},
beforeRouteLeave (to, from, next) {
console.log('beforeRouteLeave', to, from)
next()
}
通过 meta 定义路由元信息
const routes = [
{ path: '/a', component: A, meta: { title: 'Custom Title A' } }
]
使用 meta 信息动态修改标题
router.beforeEach((to, from, next) => {
console.log('beforeEach', to, from)
if (to.meta && to.meta.title) {
document.title = to.meta.title
} else {
document.title = 'default title'
}
next()
})
使用 router.addRoutes 动态添加路由
addRoute() {
this.$router.addRoutes([{
path: '/b', component: B, meta: { title: 'Custom Title B' },
}])
}
此时可以访问到 B 组件
<router-link to='/b'>to B</router-link>
git clone https://github.com/PanJiaChen/vue-element-admin
cd vue-element-admin
npm i
npm run dev
删除 src/views 下的源码,保留:
dashboard:首页
error-page:异常页面
login:登录
redirect:重定向
对 src/router/index 进行相应修改
删除 src/router/modules 文件夹
删除 src/vendor 文件夹
vue-element-admin 初始化项目
vue-element-admin 实现了登录模块,包括 token 校验、网络请求等
通过 src/settings.js 进行全局配置:
页面标题 - 站点标题
如果需要进行源码调试,需要修改 vue.config.js:
config
// https://webpack.js.org/configuration/devtool/#development
.when(process.env.NODE_ENV === 'development',
config => config.devtool('cheap-source-map')
)
将 cheap-source-map 改为 source-map,如果希望提升构建速度可以改为 eval
通常建议开发时保持 eval 配置,以增加构建速度,当出现需要源码调试排查问题时改为 source-map
api:接口请求
assets:静态资源
components:通用组件
directive:自定义指令
filters:自定义过滤器
icons:图标组件
layout:布局组件
router:路由配置
store:状态管理
styles:自定义样式
utils:通用工具方法
auth.js:token 存取
permission.js:权限检查
request.js:axios 请求封装
index.js:工具方法
views:页面
permission.js:登录认证和路由跳转
settings.js:全局配置
main.js:全局入口文件
App.vue:全局入口组件
npm init -y
安装依赖
npm i -S express
创建 app.js
const express = require('express')
// 创建 express 应用
const app = express()
// 监听 / 路径的 get 请求
app.get('/', function(req, res) {
res.send('hello node')
})
// 使 express 监听 5000 端口号发起的 http 请求
const server = app.listen(5000, function() {
const { address, port } = server.address()
console.log('Http Server is running on http://%s:%s', address, port)
})
中间件是一个函数,在请求和响应周期中被顺序调用
const myLogger = function(req, res, next) {
console.log('myLogger')
next()
}
app.use(myLogger)
提示:中间件需要在响应结束前被调用
应用如何响应请求的一种规则
响应 / 路径的 get 请求:
app.get('/', function(req, res) {
res.send('hello node')
})
响应 / 路径的 post 请求:
app.post('/', function(req, res) {
res.send('hello node')
})
通过自定义异常处理中间件处理请求中产生的异常
app.get('/', function(req, res) {
throw new Error('something has error...')
})
const errorHandler = function (err, req, res, next) {
console.log('errorHandler...')
res.status(500)
res.send('down...')
}
app.use(errorHandler)
使用时需要注意两点:
安装 boom 依赖:
npm i -S boom
创建 router 文件夹,创建 router/index.js:
const express = require('express')
const boom = require('boom')
const userRouter = require('./user')
const {
CODE_ERROR
} = require('../utils/constant')
// 注册路由
const router = express.Router()
router.get('/', function(req, res) {
res.send('欢迎学习管理后台')
})
// 通过 userRouter 来处理 /user 路由,对路由处理进行解耦
router.use('/user', userRouter)
/**
* 集中处理404请求的中间件
* 注意:该中间件必须放在正常处理流程之后
* 否则,会拦截正常请求
*/
router.use((req, res, next) => {
next(boom.notFound('接口不存在'))
})
/**
* 自定义路由异常处理中间件
* 注意两点:
* 第一,方法的参数不能减少
* 第二,方法的必须放在路由最后
*/
router.use((err, req, res, next) => {
const msg = (err && err.message) || '系统错误'
const statusCode = (err.output && err.output.statusCode) || 500;
const errorMsg = (err.output && err.output.payload && err.output.payload.error) || err.message
res.status(statusCode).json({
code: CODE_ERROR,
msg,
error: statusCode,
errorMsg
})
})
module.exports = router
创建 router/use.js:
const express = require('express')
const router = express.Router()
router.get('/info', function(req, res, next) {
res.json('user info...')
})
module.exports = router
创建 utils/constant:
module.exports = {
CODE_ERROR: -1
}
验证 /user/info:
"user info..."
验证 /user/login:
{"code":-1,"msg":"接口不存在","error":404,"errorMsg":"Not Found"}
打开配置文件 nginx.conf:
/usr/local/etc/nginx/nginx.conf
修改一:添加当前登录用户为da
user sam da;
修改二:在结尾大括号之前添加:
include /Users/sam/upload/upload.conf;
这里 /Users/sam/upload
是资源文件路径,/Users/sam/upload/upload.conf
是额外的配置文件,当前把 /Users/sam/upload/upload.conf
配置文件的内容加入 nginx.conf 也是可行的!
修改三:添加 /Users/sam/upload/upload.conf
文件,配置如下:
server
{
charset utf-8;
listen 8089;
server_name http_host;
root /Users/sam/upload/;
autoindex on;
add_header Cache-Control "no-cache, must-revalidate";
location / {
add_header Access-Control-Allow-Origin *;
}
}
如果需要加入 https 服务,可以再添加一个 server:
server
{
listen 443 default ssl;
server_name https_host;
root /Users/sam/upload/;
autoindex on;
add_header Cache-Control "no-cache, must-revalidate";
location / {
add_header Access-Control-Allow-Origin *;
}
ssl_certificate /Users/sam/Desktop/https/da.pem;
ssl_certificate_key /Users/sam/Desktop/https/da.key;
ssl_session_timeout 5m;
ssl_protocols SSLv3 TLSv1;
ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
ssl_prefer_server_ciphers on;
}
启动 nginx 服务:
sudo nginx
重启 nginx 服务:
sudo nginx -s reload
停止 nginx 服务:
sudo nginx -s stop
检查配置文件是否存在语法错误:
sudo nginx -t
访问地址:
http://localhost:8089
https://localhost
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8