npx create-nuxt-app project
koa + axios
typescript-vuex github
element-ui
assets —资源目录
layouts —布局目录
middleware —中间件目录
plugins —插件目录
static —静态(后台)
先请求
扔个模板结构(静态渲染) asyncData(请求拿数据)
把编译的结果扔给客户端 服务器下发一个script 挂载到window下
同步到浏览器(交互) 虚拟编译和服务器扔过来的作对比, 不同重新请求
第一参数: 当前页面的上下文对象
@Component({
async asyncData({params,app,$axios}) {
console.log(params,app);
app.store.dispatch('search/setName', params.key)
return {
keysword: params.key
}
},
components: {
ECrumb
},
})
如果你使用_状态树模块化_的模式,只有主模块(即 store/index.js)适用设置该方法(其他模块设置了也不会被调用)。
[图片上传失败…(image-ac40cb-1562038525452)]
路由
npm i @nuxt/typescript -D
npm i [email protected] vue-property-decorator@7 -S
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"moduleResolution": "node",
"lib": [
"esnext",
"esnext.asynciterable",
"dom"
],
"esModuleInterop": true,
"experimentalDecorators": true,
"allowJs": true,
"sourceMap": true,
"strict": true,
"noImplicitAny": false,
"noEmit": true,
"baseUrl": ".",
"paths": {
"~/*": [
"./*"
],
"@/*": [
"./*"
]
},
"types": [
"@types/node",
"@nuxt/vue-app"
]
}
}
head layout asyncData…等 放在@Component使用
@Component({
//
head() {
return {
title: this.name
}
},
layout: 'search'
})
.vue中ts 独立出来 才能引入单独ts
index.js
import Vuex from 'vuex'
import state from './state'
import getters from './getters'
import mutations from './mutations'
import actions from './actions'
import * as search from './module/search'
import geo from './module/geo'
// webpack 中 生产模式或开发
const createStore = () => {
return new Vuex.Store({
state,
getters,
mutations,
actions,
modules: {
[search.name]: search,
geo
}
})
}
export default createStore
import { RootStateTypes } from '../types';
import { MutationTree, ActionTree } from 'vuex';
const namespaced = true;
interface stateInterface {
city: string;
}
const state: stateInterface = {
city: ''
};
export const types = {
CITY: 'CITY'
};
const mutations: MutationTree = {
[types.CITY]: (state, city: string) => {
state.city = city;
}
};
const actions: ActionTree = {
setCity({ commit }, city) {
commit(types.CITY, city);
}
};
export default { namespaced, state, actions, mutations };
背景:给 utils 目录添加别名
刚刚说到,Nuxt.js内置了 webpack 配置,如果想要拓展配置,可以在 nuxt.config.js 文件中添加。同时也可以在该文件中,将配置信息打印出来。
extend (config, ctx) {
console.log('webpack config:', config)
if (ctx.isClient) {
// 添加 alias 配置
config.resolve.alias['~src'] = __dirname
config.resolve.alias['~utils'] = path.join(__dirname, 'utils')
}
}
安装
yarn add babel-cli babel-core babel-preset-es2015 babel-preset-stage-0
修改package.json文件,在“dev”和“start”命令后面新增:–exec babel-node
项目根目录下新增babel配置文件“.babelrc”文件,写入以下配置
{
"preset": ["es2015","stage-0"]
}
serve目录
Passport 解决登陆认证的问题
Web应用一般有2种登陆认证的形式:
基于本地 配置策略 进行 用户名和密码验证
passport.js
// 身份验证
// http://blog.fens.me/nodejs-express-passport/ 解决登陆认证的问题
import passport from 'koa-passport'
import LocalStrategy from 'passport-local'
// 用户表
import UserModel from '../../dbs/models/user'
// 配置策略. (具体操作)
passport.use(new LocalStrategy(async function (username, password, done) {
let result = await UserModel.findOne({
username: username
})
// 存在
if (result != null) {
// 密码对不对
if (result.password === password) {
return done(null, result)
} else {
return done(null, false, {
msg: '密码错误'
})
}
} else {
// 不存在
return done(null, false, {
msg: '用户不存在'
})
}
}))
// 保存用户
passport.serializeUser(function (user, done) {
done(null, user)
})
// 删除用户
passport.deserializeUser(function (user, done) {
done(null, user)
})
export default passport
路由控制 user.js
相关资料
nodemailer实例
redis的使用
passport文档
ctx.session.passport.user 用户信息
ctx.request.body post传参
ctx.params ctx.query get传参
import Router from 'koa-router';
// 使用redis 验证 --- 不同用户同时发送验证码 区分不用户,不能存表(量大,内存会溢出),
import Redis from 'koa-redis';
// 给用户发邮件
import nodeMailer from 'nodemailer';
import Email from '../dbs/config';
import userModel from '../dbs/models/user';
import axios from './utils/axios';
import passport from './utils/passport';
const router = new Router();
const client = new Redis({}).client;
function err(msg: string) {
return {
code: -1,
msg
};
}
// 注册
router.post('/signup', async (ctx: any) => {
// ctx.request.body post传参
const { username, password, email, code } = ctx.request.body;
// 验证code
if (code) {
// 获取对应的code 验证码
const saveCode = await client.hget(`nodemail:${username}`, 'code');
// 过去时间
const expire = await client.hget(`nodemail:${username}`, 'expire');
if (code === saveCode) {
// 是否过期
if (Date.now() - expire > 0) {
ctx.body = {
code: -1,
msg: '验证码已过期,请重新验证'
};
return false;
}
} else {
// 验证码错误
ctx.body = {
code: -1,
msg: '验证码错误'
};
}
} else {
ctx.body = {
code: -1,
msg: '验证码不能为空'
};
}
// 验证用户是否被注册过.
try {
await userModel.findOne(username);
ctx.body = err('用户名被注册过了');
} catch {
let user = userModel.create({
username,
password,
email
});
if (user) {
// 注册后自动登录
let res = await axios.post('/signin', { username, password });
if (res.data && res.data.code === 0) {
ctx.body = {
code: 0,
data: res.data.user,
msg: '注册成功'
};
} else {
ctx.body = err('error');
}
} else {
// 创建失败
ctx.body = err('注册失败');
}
}
});
// 登录
router.post('/signin', (ctx: any, next: any) => {
// 登录 验证
return passport.authenticate(`local`, function(
error: any,
user: any,
info: any
) {
if (error) {
ctx.body = err(error);
return false;
}
if (user) {
ctx.body = {
code: 0,
msg: '登录成功',
data: user
};
// passport 登录用户初始化session
return ctx.login(user);
} else {
ctx.body = {
code: 1,
msg: info
};
}
})(ctx, next);
});
// 验证
router.post('/verify',async (ctx: any,next: any) => {
let {username,email} = ctx.request.body
// 阻止频繁访问
let expire = await client.hget(`nodemail:${username}`, 'expire');
if(expire && (Date.now() - expire) < 0) {
ctx.body = {
code: -1,
msg: '请求过于频繁'
}
return false
}
// 邮件配置
let transporter = nodeMailer.createTransport({
host: Email.smtp.host,
post: Email.smtp.port,
// 监听其他端口(原: 465)
secure: false,
auth: {
user: Email.smtp.user,
// 授权码
pass: Email.smtp.pass
}
})
// 新建一个验证码信息
let ko = {
code: Email.code(),
expire: Email.expire(),
user: username,
email: email,
}
// 邮件信息配置
let mailOptions = {
from: `认证邮件<${Email.smtp.user}>`,
to: ko.email,
// 标题
subject: `网站的注册码`,
// 发送的text或者html格式
html: `你的验证码是${ko.code}`
}
// 发送
await transporter.sendMail(mailOptions, (error,info) => {
if(error) {
return console.log(error)
}
// hmset 为散列里面的一个或多个键设置值 OK hmset('hash-key', obj)
client.hmset(`nodemail:${ko.user}`, ko)
})
ctx.body = {
code: 0,
msg: `验证码已发送, 有效期1min`
}
})
router.post(`/exit`, async (ctx,next) => {
// passport 删除该用户session
await ctx.logout()
// 二次验证是否退出 passport的验证
// isAuthenticated: 测试该用户是否存在于session中(即是否已登录)
if(ctx.isAuthenticated()) {
ctx.body = err('退出失败')
}else{
ctx.body = {
code: 0
}
}
})
// 获取用户信息
router.get('/user', async (ctx) => {
if(ctx.isAuthenticated()) {
let {username,email} = ctx.session.passport.user
ctx.body = {
code: 0,
user: username,
email
}
}else{
ctx.body = {
code: -1,
user: '',
email: ''
}
}
})
export default router
app.js
npm install koa-bodyparser koa-generic-session koa-json koa-passport passport-local
// 引入mongoose redis
import mongoose from 'mongoose'
// 处理passport相关请求
import bodyParser from 'koa-bodyparser'
// session删写
import session from 'koa-generic-session'
import Redis from 'koa-redis'
// 代码格式化. 打印.
import json from 'koa-json'
import dbsConfig from './dbs/config'
import Passpot from './interface/utils/passport'
import UserInterface from './interface/user'
import passport from './interface/utils/passport';
// session加密处理的两字符
app.keys = ['keys','key']
app.proxy = true
// 存储
app.use(session({
key: 'egg-mt',
prefix: 'mt:uid',
store: new Redis()
}))
app.use(bodyParser({
enbleTypes: ['text','json','form']
}))
app.use(json())
// 连接数据库
mongoose.connect(dbsConfig.dbs, {
useNewUrlParser: true
})
app.use(passport.initialize())
app.use(passport.session())
// 添加路由
crypto-js (加密算法类库)
相关讲解
shims-vue.d.ts
import VueRouter, { Route } from "vue-router";
import Vue from 'vue';
declare var document: Document;
declare module '*.vue' {
export default Vue;
}
declare module "*.ts" {
const value: any;
export default value;
}
declare global {
interface window {
require: any;
}
}
// 识别 this.$route
declare module 'vue/types/vue' {
interface Vue {
$router: VueRouter; // 这表示this下有这个东西
$route: Route;
$notify: any;
}
}
在根目录的 tsconfig.json 里面加上 “noImplicitThis”: false ,忽略 this 的类型检查
“noImplicitThis”: false,
JS实现中文转拼音(首字母大写和首字母简拼)
js-pinyin
地图组件
map.vue
另一种显示方式: 标注图层
示例
单条数据格式
console.log(JSON.stringify(LabelsData[6]))
{
"name": "京味斋烤鸭店",
"position": [116.462483, 39.992492],
"zooms": [10, 20],
"opacity": 1,
"zIndex": 4,
"icon": {
"type": "image",
"image": "https://a.amap.com/jsapi_demos/static/images/poi-marker.png",
"clipOrigin": [547, 92],
"clipSize": [50, 68],
"size": [25, 34],
"anchor": "bottom-center",
"angel": 0,
"retina": true
},
"text": {
"content": "京味斋烤鸭店",
"direction": "top",
"offset": [0, 0],
"style": {
"fontSize": 15,
"fontWeight": "normal",
"fillColor": "#666",
"strokeColor": "#fff",
"strokeWidth": 1
}
},
"extData": {
"index": 6
}
}
// 滚动距离
let scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
mounted() {
window.addEventListener("scroll", this.handleScroll, true); // 监听(绑定)滚轮滚动事件
}
// 滚动事件
handleScroll(e) {
}
destroyed() {
window.removeEventListener("scroll", this.handleScroll); // 监听(绑定)滚轮滚动事件
}