技术选型

使用vue-cli脚手架搭建了项目

一.首先安装node.js

Vue CLI 4.x 安装需要 nodeJs ≥ 8.9 (官方推荐 8.11.0+,你可以使用 nvm 或 nvm-windows在同一台电脑中管理多个 Node 版本)

下载安装nodeJs(和其他系统软件一样根据需求按步骤安装)这里不做介绍, 中文官方下载地址:http://nodejs.cn/download/

查看node版本:node -v

二.安装vue cli 指令

npm install -g @vue/cli

三.如果比较慢可以配置配置淘宝镜像

可以使进入项目时加载速度快一些 npm config set registry https://registry.npm.taobao.org

使用了axios实现了前后端的通信,搭配的Promise将axios进行了二次封装

安装 cnpm install axios --save

import store from '@/store/index'

// 基于环境变量配置路径

var ips = {

development: 'http://websong.wang:4000',

production: "http://192.168.200.23"

}

const url = ips[process.env.NODE_ENV]

// 用axios重新生成了请求的实例

const server = axios.create({

baseURL: url, // 项目发送axios请求的公共地址

timeout: 5000 // 请求超时时间 这里是请求超过五秒后还没有获得请求结果 提示请求超时

})

server.interceptors.request.use(config => {

config.headers['token'] = store.state.tokenMsg.token

return config // 将配置完成的token返回 如果不返回 请求不会继续进行

}, err => {

Promise.reject(err) // 使用promise将错误信息返回出去

})

// axios 接受到服务器响应信息后的配置

// response 是响应的意思 这里的意思是使用响应拦截

server.interceptors.response.use(res => {

// 每次请求完毕以后,重新获取token值:必须是在登录后才能刷新 token 过期重新请求 无痛刷新token

if (store.state.tokenMsg.token) {

var {

id,

username,

islogin

} = store.state.tokenMsg.user;

axios

.get(url + "/user/token", {

params: {

uid: id, // 用户 id

username, // 用户名字

islogin, // 是否登录

},

})

.then(({

data

}) => {

console.log(data)

if (data.code) {

// 重新吧token赋值

store.commit("updataToken", data.result.token);

}

});

}

// res包含了服务器返回的所有响应信息 其实就是服务器返回给你的东西

return res.data

}, err => {

// 当服务器响应产生错误时的回调函数

console.error(err) // 这里将服务器发生错误的错误信息打印在控制台中

})

export default server

amfe-flexible postcss-pxtorem 实现css样式里的px转换为rem

Vant 中的样式默认使用 px 作为单位,如果需要使用 rem 单位,推荐使用以下两个工具:

postcss-pxtorem 是一款 postcss 插件,用于将 px 单位转化为 rem lib-flexible 用于设置 rem 基准值

1.安装依赖

yarn add amfe-flexible

或者使用

npm i -S amfe-flexible

然后在 main.js 中加载执行该模块:

import 'amfe-flexible'

2.安装postcss-pxtorem依赖:

yarn add -D postcss-pxtorem

或者是

npm install postcss-pxtorem -D

这里解释一下:# -D 是 --save-dev 的简写 把依赖包的版本信息写进 package.json 文件里面

然后在项目根目录中创建 postcss.config.js 文件:

module.exports = {

plugins: {

// postcss-pxtorem 插件的版本需要 >= 5.0.0

'postcss-pxtorem': {

rootValue({ file }) { // 判断是否是vant的文件 如果是就使用 37.5为根节点字体大小

// 否则使用75 因为vant使用的设计标准为375 但是市场现在的主流设置尺寸是750

return file.indexOf('vant') !== -1 ? 37.5 : 75;

},

// 配置哪些文件中的尺寸需要转化为rem *表示所有的都要转化

propList: ['*'],

},

},

};

这个文件会被自执行

vant-ui 实现的页面布局

npm i vant@latest-v2 -S

main.js 文件

引入

import Vant from 'vant';

import 'vant/lib/index.css';

Vue.use(Vant);

vue-router实现的路由导航

npm i vue-router

使用vuex解决项目数据共享,搭配vuex插件 vuex-persist 实现了本地存储

npm install --save vuex@next // 安装vuex

yarn add vuex-persistedstate --save // vuex固化工具存储本地存储的

import persist from 'vuex-persistedstate'

plugins: [persis()]

plugins: [persist({

storage: localStorage, // 定义数据存储的方式 值可以是localStorage 也可以是sessionStorage

reducer(state) { //定义需要指定存储的数据 因为正常工作中vuex不是每一条数据都需要存储 所以我们需要指定需要存储的数据

return {

goods: state.goods

}

}

})]

使用lodash库扩展了方法

npm i --save lodash

node.js var _ = require('lodash');

vue import _ from 'lodash'

用animate.css插件库实现较为复杂的动画

网址 https://animate.style/

npm install animate.css // 安装

import 'animate.css' // 引入

:duration="{ enter: 500, leave: 1000 }"

name="animate__animated animate__bounce"

enter-active-class="animate__zoomInDown"

leave-active-class=" animate__zoomOutDown"

>

// 包住组件 如果是router-view 就是transition 使用换类名即可

// enter 进入时间 leave 离开时间

使用sass作为css的预处理器

npm install --save-dev sass-loader

//sass-loader依赖于node-sass

npm install --save-dev node-sass

使用第三方dayjs插件做事件格式化的处理

npm install dayjs --save

import dayjs from 'dayjs'

dayjs(String)

dayjs('1995-12-25')

https://blog.csdn.net/u014225733/article/details/90602290

注释插件

网址 : https://vuese.github.io/website/zh/cli/#methods

可以根据注释生成文档,可以让你养成良好的注释代码习惯,如果你按照他个规则来注释可以 快速成成文档

命令 : yarn global add @vuese/cli

在package.json中写入运行命令

"scripts": {

"doc": "vuese gen",

},

运行: npm run doc

会在根目录下生成一个website 文件夹里面 有个 index.HTML 打开会有一个文档

前端的性能优化

使用cdn静态资源托管

静态资源托管网站 : https://cdn.baomitu.com/

就是把 import 导入的都给删除了然后换成 cdn 静态资源托管

吧 vuex 和 stare 和 router 删除吧以上 cdn 放入我们的 public 里面的 index.html body标签下面即可

在 vue.config 文件夹里面写入

return {

externals: {

vue: "Vue",

"vue-router": "VueRouter",

vuex: "Vuex",

axios: "axios",

},

};

vant-ui的按需引入

新建一个 piugins 文件夹里面引入 vantS.js 文件里面写入

// 一个对象向外暴露一个install 的函数

import {

SwipeItem,

Swipe,

} from 'vant'

var list = [ SwipeItem, Swipe ];

export default {

install(Vue, option) {

// vant组件的按需引入

list.forEach((item) => {

Vue.component(item.name, item);

});

},

};

// 放在 main.js 文件里面

import piugins from '../src/piugins/vantS'

Vue.use(piugins);

长列表的优化

import { throttle } from 'lodash' // 引入lodash 要不自己手写

mounted() {

// 监听页面的滚动到底行为

window.onscroll = throttle(() => {

// 是否到达页底部

var doc = document.documentElement

/*

scrollHeight(文档内容实际高度,包括超出视窗的溢出部分)

scrollTop(滚动条卷进去距离)、

clientHeight(窗口可视范围高度)

当 clientHeight + scrollTop >= scrollHeight 时,表示已经抵达内容的底部了,可以加载更多内容。

scrollHeight:可以通过 document.documentElement.scrollHeight 、document.body.scrollHeight 获取;

clientHeight:可以通过window.innerHeight 、 document.documentElement.clientHeight 获取;

scrollTop:可以通过window.pageYOffset 、 document.documentElement.scrollTop 获取;

*/

if (doc.scrollHeight - 200 <= doc.clientHeight + doc.scrollTop) {

this.$emit('onload') // 到底了

}

}, 1000)

},

骨架屏

在我们的数据没有加载过来的时候可以用骨架屏来站一下空白空间

封装我们的骨架屏,可以用 vant ui 的骨架屏也可以自己用 css3的动画手写骨架屏

然后在通过 vue.component 全局注册我们的组件,用v-if来做判断有数据的时候显示商品没有商品的时候显示骨架屏

图片懒加载自定义指令

Vue.directive('lazy', (el, bing) => {

console.log(el)

// new IntersectionObserver 创建我们的交叉观察器

let ob = new IntersectionObserver(

(changes) => {

// changes 是个数组包含所有的监听对象和视口的交叉位置

changes.forEach((item) => {

if (item.isIntersecting) { // item.isIntersecting == 1 为 true

// 当先监听的对象 符合条件

let lazyImg = item.target; // 拿到当前监听的盒子

// 改变src

el.src = bing.value;

// 加载完移除监听

ob.unobserve(lazyImg);

}

});

}, {

// threshold 控制交叉状态在什么养的情况下触发上面的回调

threshold: [1], // 0 一露头 0.5 露一半 1 完全出现

}

);

// 使`IntersectionObserver`开始监听一个目标元素。

ob.observe(el);

})

vue.config.js 配置去掉 console.log

npm i -D terser-webpack-plugin

const { defineConfig } = require("@vue/cli-service");

const TerserPlugin = require("terser-webpack-plugin");

module.exports = defineConfig({

// 打包后的二级目录地址

publicPath: "/a123456",

transpileDependencies: true,

devServer: {

open: true,

port: 3000,

// webpack项目被托管需要开启这个

allowedHosts: "all",

},

// 当npm build的时候 关闭源码映射

productionSourceMap: false,

configureWebpack: (config) => {

// 只有npm run build的时候 才是production

if (process.env.NODE_ENV === "production") {

return {

plugins: [

//打包环境去掉console.log

new TerserPlugin({

// 多进程:加速打包速度

parallel: true,

terserOptions: {

ecma: undefined,

warnings: false,

parse: {},

compress: {

drop_console: true,

drop_debugger: false,

pure_funcs: ["console.log"], // 移除console

},

},

}),

],

};

}

// cdn托管

return {

externals: {

vue: "Vue",

"vue-router": "VueRouter",

vuex: "Vuex",

axios: "axios",

},

};

},

});

环境变量的配置

在个目录下面新建一个 .env.development 文件 写入以下

NODE_ENV=development

在个目录下面新建一个 .env.production文件 写入以下

NODE_ENV=production

// 基于环境变量配置路径

var ips = {

development: 'http://websong.wang:4000',

production: "http://192.168.200.23"

}

console.log(process.env.NODE_ENV)

const url = ips[process.env.NODE_ENV]

全选和反选的逻辑

//遍历复选框

class="checkStyle"

v-model="item.checked"

>

全选

checkedAll: {

get(val) {

return this.cartResult.every((item) => item.checked);

},

set(setVal) {

this.cartResult.forEach((item) => {

item.checked = setVal;

});

},

},

计算价格

priceAll() { // reduce 计算选中的价格

return (

this.cartResult.reduce(

(num, item) => (num += item.checked && item.count * item.price),

0

) * 100

);

},

项目的业务逻辑

文件的上传显示进度条

// 箭头函数

cutDown({ file }) {

var data = new FormData()

data.append('inputFile', file)

var pross = this.$refs.pross

// 发起提交

Axios.post('/up', data, {

// 监听上传的进度条事件

onUploadProgress(event) {

var value = Math.floor((event.loaded / event.total) * 100)

pross.innerHTML = value + '%'

pross.style.width = value + '%'

},

}).then(({ data }) => {

console.log(data)

})

},

vue 的过滤器的使用

// 全局过滤器

import Vue from "vue";

import day from "dayjs";

// 取小数位

Vue.filter("float", (n, set) => {

// 1. 判断

if (isFinite(n)) {

return Number(n).toFixed(set || 2);

} else {

return n;

}

});

// 加货币单位

Vue.filter("$", (n, $) => {

return n + ($ || "元");

});

// 格式化时间

Vue.filter("date", (date) => {

return day(date).format("MM-DD");

});

路由的判断

// 需要拦截的页面 配置一个 meta 属性就可以了

{

path: "login",

meta: {

// 有true表示需要登录才能看

auth: false,

},

component: () => import("@/views/User/Login"),

},

// 全局的前置路由守卫--路由的拦截器--路由的生命周期--路由的钩子函数

router.beforeEach((to, from, next) => {

// next 有几种用法:字符串地址,对象,布尔值 无论怎样next必须执行

var token = store.getters.token;

var auth = to.meta.auth;

// 如果需要登录,并且登录了,让它进入

if ((token && auth === true) || (!token && auth === false)) {

next();

// 如果登录了,不需要登录才能访问,则不让进入

} else if ((token && auth === false) || (!token && auth === true)) {

next(false);

// 没有登录,不需要登录才能访问,则让进入

} else {

next();

}

});

// 路由后置守卫

router.afterEach((to, from) => {

// 预先请求数据

// 上传日志:用户的操作行为,用户是从哪个时间点进入这个页面的。

});

根据权限限制点击拦截

第一步:写一个v-off 指令传一个

// tabBar 拦截

v-off="{

to: to,

path: '/User',

}"

to="/User"

icon="setting-o"

>我的

>

methods: {

to(path) {

this.$router.push(path);

},

},

// 指令是有生命周期的

Vue.directive("isLogin", {

// 挂载完成

inserted(el, bing) {

el.addEventListener("click", (e) => {

// 判断一下是否有登录的记录

if (store.state.userInfo.token) {

// 这里是灵活的,自己定义的

var { action, path } = bing.value;

action(path);

} else {

// 如果没有登录,提示用户去登录

Dialog.confirm({

title: "提示",

message: "您需要先登录才能继续操作,是否去登录?",

})

.then(() => {

router.push("/user/login");

})

.catch(() => {

// on cancel

});

}

// 如果没有就停止继续

});

},

});

动态路由

动态路由是什么?

一般我们使用vue-router配置路由时,都是直接配置固定的路径

而动态路由是我们可以在 vue-router 的路由路径中使用“动态路径参数”

让路径不再是固定的。

比如在写一个商品详情页面的时候,我们的页面结构都一样,只是渲染的数据不同而已,这时候就可以根据商品的不同id去设置动态路由,只需要写一个组件,就可以把每个商品的商品详情映射到同一个组件上去。

首先我会在 router 的配置项里面

const routes = [

{

path: "/",

component: () => import("@/views/Main.vue"),

redirect: "/index",

children: [

{

path: "/index/:id",

component: () => import("@/views/Index.vue"),

},

],

},

];

超级重点:一个“路径参数”使用冒号 : 标记。当匹配到一个路由时,参数值会被设置到 this.$route.params,可以在每个组件内使用。

methods: {

goPage(val) {

this.state = val;

this.$router.push({

path: "/index2/" + val,

});

},

},

客服的sockit长链接

webSocket 的概念

webSocket 协议是基于TCP的一种新的网络协议,它实现了浏览器与服务器全双工网络通信 允许服务器主动发送信息给客户端,可以实时连接

websocket 是一种持久的协议,http是非持久协议,也就是长链接

早期的话通信:a向服务器发送一次消息,服务器就是每隔1秒就向服务器发送一次请求,看看有没有a的消息,如果有a的消息就拿过来没有就一直请求 非常消耗性能

原生的 websocket 比较的麻烦 而且只能传字符串 不能传对象还得来回转换,方法也很少,比如广播事件都没有,还得自己封装 (自封封装还是通过便利)

安装的地址 https://www.npmjs.com/package/nodejs-websocket

安装依赖 npm install nodejs-websocket

现在用的是 socketio

> npm install express@4

> npm install socket.io

// 一上来用他来监听

// 第一步 先导入我们的 socket.io 模块 在去 new Server(server); 后端可以用 io.on 监听客户端发来的数据 用 emit 来派发给前端数据

const express = require('express');

const app = express();

const http = require('http');

const server = http.createServer(app);

const {

Server

} = require("socket.io");

const io = new Server(server);

app.get('/', (req, res) => {

res.sendFile(__dirname + '/index.html');

});

// 一上来用他来监听

io.on('connection', (socket) => {

console.log('a user connected');

// 后端监听传来的消息

socket.on('hi', (msg) => {

console.log(msg)

// 向前端发送信息

socket.emit('say', {

say: '你在说什么小伙子'

})

})

});

server.listen(3000, () => {

console.log('listening on *:3000');

});

// 连接服务端

var arr = []

var socket = io();

let input = document.querySelector('#input')

btn.addEventListener('click', function () {

// 向服务器发起一个 socket 消息

socket.emit('hi', {

// to:'', 可以指定告诉人

msg: input.value

})

socket.on('say', (say) => {

console.log(say)

arr.push(say)

})

sm.innerHTML(

`

wda

`

)

})

js调节移动端响应式的屏幕尺寸

1.设置当前html文件的字符编码

是用于设定禁止浏览器从本地机的缓存中调阅页面内容,设定后一旦离开网页就无法从Cache中再调出

禁止将页面中的一连串数字识别为电话号码、并设置为手机可以拨打的一个连接。这个标签的默认值是telephone=yes。

7.删除默认的苹果工具栏和菜单栏 当我们需要显示工具栏和菜单栏时,这个行meta就不用加了,默认就是显示。

8.控制状态栏显示样式 content设置状态栏颜色

.条件注释

- html5shiv让浏览器可以识别html5的新标签;

- respond让低版本浏览器可以使用CSS3的媒体查询。

纯前端模糊搜索防抖

先把 数据备份一份 也就是在复制一份 arr渲染的数据 arr1 备份数据 searchValue 搜索的value

arr = arr1.filter(item => searchValue.includes(item.name))

文件的上传

文件上传的底层原理

input type 的file 属性 原生 input 拿到这个 file.target.files[0] 上传

vant 的文件上传

afterRead( {file} ) { // vant 的是

// 此时可以自行将文件上传至服务器

// 先判断大小

if (file.size / 1024 >= 30) {

return alert('超出30kb了')

}

// 类型判断

if (!/image/.test(file.type)) {

return alert('类型不允许')

}

// 重点:上传文件使用formData

var data = new FormData()

data.append('inputFile', file)

var pross = this.$refs.pross

// 发起提交

Axios.post('/up', data, {

// axios第三个参数是可以监听 上传的进度条事件

onUploadProgress(event) {

var value = Math.floor((event.loaded / event.total) * 100)

pross.innerHTML = value + '%' // 设置倒计时

pross.style.width = value + '%' // 写一个div设置width 度

},

}).then(({ data }) => {

console.log(data)

})

},

项目中碰到的问题

props的返回值

list: {

// 约束类型

type: Array,

// 可以设置默认值

// 如果值类型是对象或数组,需要使用函数返回

default: function () {

return []

},

},

keep-alive 的缓存问题太深的组件嵌套失效了

路由跳转报错

重复性跳转

vue-router@3.0版本及以上回调形式已经改成promise api的形式了,返回的是一个promise,如果路由地址跳转相同, 且没有捕获到错误,控制台始终会出现如图所示的警告 (注:3.0以下版本则不会出现以下警告!!!,因路由回调问题…)

// 重写 push 结局跳转不传参报错的问题

import VueRouter from "vue-router";

const originalPush = VueRouter.prototype.push;

VueRouter.prototype.push = function push(location) {

return originalPush.call(this, location).catch((err) => err);

};

配置 websocket 安装

直接安装是安装不上的

在package.json 里面配置

“socket.io-client”: “2.3.0”,

在执行 npm i

.git文件删除怎么了

1、我们找到原来的项目,

2、把原来之前的项目给 clone 下来

3、克隆下来之后,把原来之前的 .git 文件给剪切然后 再放到没有 .git 的文件里

远程仓库的路径做更改,git提交远程提交不上

$ git push 报错

[session-4b46a982] Auth error: Access deined: authorize failure.

fatal: Could not read from remote repository.

Please make sure you have the correct access rights

and the repository exists.

git remote -v 查看提交的地址 改掉

找到git的配置文件

在找到 config 配置文件把他的

改一下 origin 里的 url 你的更改后的项目地址

后端node

用 node.js 和 expres 框架搭建一个小服务器

//require表示引包,引包就是引用自己的一个特殊功能

var http = require("http");

var url = require("url");

var server = http.createServer(function(req,res){

//得到查询部分,由于写了true,那么就是一个对象

var queryObj = url.parse(req.url,true).query;

var name = queryObj.name;

var age = queryObj.age;

var sex = queryObj.sex;

res.writeHead(200,{"Content-Type":"text/html;charset=UTF-8"});

res.end("服务器收到了表单请求" + name + age + sex);

});

//运行服务器,监听3000端口(端口号可以任改)

server.listen(3000,"127.0.0.1");

// 终端打印如下信息

console.log('Server running at http://127.0.0.1:3000/')

后端全选删除接口

/*

第一步用 req.query 拿到前端发送过来的数据

用 remove 删除 指定的id 只要满足$in 拿 [] 里面的元素 都可以查询出来 查询出来删除

*/

const {

addGoods

} = require('../DB')

module.exports = function (router) {

router.post('/', (req, res) => {

let

txt = req.body

console.log(txt)

addGoods.remove({

_id: {

$in: txt

},

}).then(() => {

res.send({

code: 200,

msg: '删除购物车成功',

})

}).catch(() => {

res.send({

code: 404,

msg: '删除购物车失败',

})

})

})

return router

}

登录的接口

1.下载我们的 jsonwebtoken 包

用send 发送到前端

token: jwt.sign({

uid: data._id

}, pub, {

expiresIn: '.5h'

})

const {

db_list

} = require('../DB')

2 当用户输入账号和密码发送到服务端 我用 find 在我们的 mongoodb 数据库去查找我们的数据如果查找到的就返回我们前端数据 把 token范过期,这里用 jwt.verify判断token是否解密成功 解密成功 用try catch

// 首先引入我们的 token 包

var jwt = require('jsonwebtoken');

// 自己写一个类似于密码的东西

var pub = 'asodh23hdhhodh9sahdosahdhsadhsfhdsofodsfh'

module.exports = function (router) {

router.get('/', (req, res) => {

// req.get 是可以拿到前端 headers 请求头的数据

// jwt.verify 把前端headers里面传过来的一个token进行解密

console.log(req.get('token'))

// try {

// jwt.verify(req.get('token')) // 如果解密成功就会返回一个 {}

// } catch (e) {

// // 如果没有 token 就 try catch 给他抛出去

// res.json({

// error: e

// })

// }

// goods 是你上面定义的字段根据这个查找

db_list.find({

username: req.query.username,

password: req.query.password,

}).then((data) => {

// 判断返回的数据 是否有长度如果有长说明找到了

if (data.length > 0) {

res.send({

code: 200,

msg: '登录成功',

// expiresIn1 表示一个小时 然后 .5h 是表示半个小时

// pub 自己设置的密码

// 返回的 id data._id

token: jwt.sign({

uid: data._id

}, pub, {

expiresIn: '.5h'

})

})

} else {

res.send({

code: 404,

msg: '登录失败,该用户不存在',

})

}

})

})

return router

}

注册的接口

let {

db_list

} = require('../DB')

module.exports = function (router) {

router.get('/', (req, res) => {

console.log(req)

db_list.find({

username: req.query.username

}).then((data) => {

console.log(data)

// 说明找到可

if (data.length > 0) {

res.send({

code: 405,

msg: '该用户名已经注册',

})

} else {

// goods 是你上面定义的字段根据这个查找

db_list.create(req.query).then(() => {

res.send({

code: 200,

msg: '注册成功',

})

}).catch(() => {

res.send({

code: 404,

msg: '注册失败',

})

})

}

})

})

return router

}

修改用户的接口

let {

order

} = require('../DB')

module.exports = function (router) {

router.get('/', (req, res) => {

// 修改商品的接口

order.update({

_id: req.query._id

}, {

$set: req.query

}).then(() => {

res.send({

state: 200,

success: true,

message: '修改商品成功'

})

}).catch(() => {

res.send({

state: 404,

success: false,

message: "修改商品成功"

})

})

})

return router

}

搜索商品

const {

goods

} = require('../DB')

module.exports = function (router) {

router.get('/', async (req, res) => {

// goods 是你上面定义的字段根据这个查找

let reg = new RegExp(req.query.goods_name)

var data = await goods.find({

goods_name: reg

})

if (data) {

res.send({

result: data,

code: 200,

msg: '查找成功',

})

} else {

res.send({

code: 404,

msg: '查找失败',

})

}

})

return router

}

项目的代码地址

全栈移动段项目

推荐链接

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。