vue.js是一套构建用户界面的渐进式框架。vue采用自底向上增量开发的设计。vue的核心库只关心视图层,非常容易学习,非常容易与其它库和已有项目整合。vue完全有能力驱动采用单文件组件和vue生态系统支持的库开发的复杂单页应用。
vue.js的目标是通过尽可能简单的API实现响应的数据绑定和组合的视图组件。
总体框架
一个vue-cli的项目结构如下,其中src文件夹是需要掌握的,所以本文也重点讲解其中的文件,至于其他相关文件,了解一下即可。
1.build——[webpack配置]
build文件主要是webpack的配置,主要启动文件是dev-server.js,当我们输入npm run dev首先启动的就是dev-server.js,它会去检查node及npm版本,加载配置文件,启动服务。
2.config——[vue项目配置]
config文件主要是项目相关配置,我们常用的就是当端口冲突时配置监听端口,打包输出路径及命名等
3.node_modules——[依赖包]
node_modules里面是项目依赖包,其中包括很多基础依赖,自己也可以根据需要安装其他依赖。安装方法为打开cmd,进入项目目录,输入npm install [依赖包名称],回车。
在两种情况下我们会自己去安装依赖:
(1)项目运行缺少该依赖包:例如项目加载外部css会用到的css-loader,路由跳转vue-loader等(安装方法示例:npm install css-loader)
(2)安装插件:如vux(基于WEUI的移动端组件库),vue-swiper(轮播插件
注:有时会安装指定依赖版本,需在依赖包名称后加上版本号信息,如安装11.1.4版本的vue-loader,输入npm install [email protected]
4.src——[项目核心文件]
项目核心文件前面已经进行了简单的说明,接下来重点讲解main.js,App.vue,及router的index.js
4.1 index.html——[主页]
index.html如其他html一样,但一般只定义一个空的根节点,在main.js里面定义的实例将挂载在根节点下,内容都通过vue组件来填充
4.2 App.vue——[根组件]
一个vue页面通常由三部分组成:模板(template)、js(script)、样式(style)
【template】
其中模板只能包含一个父节点,也就是说顶层的div只能有一个(例如上图,父节点为#app的div,其没有兄弟节点)
打一个比喻吧,
【script】
vue通常用es6来写,用export default导出,其下面可以包含数据data,生命周期(mounted等),方法(methods)等,具体语法请看vue.js文档,在后面我也会通过例子来说明。
【style】
样式通过style标签包裹,默认是影响全局的,如需定义作用域只在该组件下起作用,需在标签上加scoped,
如要引入外部css文件,首先需给项目安装css-loader依赖包,打开cmd,进入项目目录,输入npm install css-loader,回车。安装完成后,就可以在style标签下import所需的css文件,例如:
这样,我们就可以把style下的样式封装起来,写到css文件夹,再引入到页面使用,整个vue页面也看上去更简洁。
4.3 main.js——[入口文件]
main.js主要是引入vue框架,根组件及路由设置,并且定义vue实例,下图中的
components:{App}就是引入的根组件App.vue
后期还可以引入插件,当然首先得安装插件。
4.4 router——[路由配置]
router文件夹下,有一个index.js,即为路由配置文件
这里定义了路径为'/'的路由,该路由对应的页面是Hello组件,所以当我们在浏览器url访问http://localhost:8080/#/时就渲染的Hello组件
类似的,我们可以设置多个路由,‘/index’,'/list'之类的,当然首先得引入该组件,再为该组件设置路由。
①入口文件main.js——②入口文件中配置了相关的模板注入信息——③扫描路由。先找到公共路由,再根据注入的模板信息扫描模块中的路由。
①发送请求——②根据请求路由到相关页面——③页面创建后调用钩子函数调用需要用的js方法——④js方法访问API方法——⑤根据API访问后台接口——⑥后台接口返回相关数据——⑦修改数据模型——⑧数据模型双向绑定。
①公共路由与模块路由启动后被扫描。
②根据访问路径查找相关路径对应信息:
a、先找到父路径:path: '/saas-clients',//父路径
b、找到子路径:path: 'index'
c、根据子路径跳转视图地址:component: _import('saas-clients/pages/index'), //跳转的vue视图
d、找到module-saas-clients模块目录下的pages目录下的index.vue文件
e、跳转地址的主目录为saas-clients,而并非module-saas-clients是如何对应的?
根据公共路由中import_development.js、import_production.js2个文件的配置,根据vue项目规则自动加的前缀。
我的项目中使用了scss,elementui官网提供了解决方案如下:
在项目中改变 SCSS 变量
Element 的 theme-chalk 使用 SCSS 编写,如果你的项目也使用了 SCSS,那么可以直接在项目中改变 Element 的样式变量。
1. 新建一个样式文件,例如 element-variables.scss,写入以下内容:
/* 改变主题色变量 */
$--color-primary: teal;
/* 改变 icon 字体路径变量,必需 */
$--font-path: '~element-ui/lib/theme-chalk/fonts';
@import "~element-ui/packages/theme-chalk/src/index";
2. 之后,在项目的入口文件中,直接引入以上样式文件即可(无需引入 Element 编译好的 CSS 文件):
import Vue from 'vue'
import Element from 'element-ui'
import './element-variables.scss'
Vue.use(Element)
需要注意的是,覆盖字体路径变量是必需的,将其赋值为 Element 中 icon 图标所在的相对路径即可。
以上方法,我们修改主题色变量 $--color-primary 的值,然后在项目入口文件 main.js 中引入该样式文件,覆盖elementui的css文件,即可实现换肤。
那么怎么做到自定义任意颜色的主题呢?大致思路如下:
实际步骤如下:
内容如下:
/* theme color */
$--color-primary: #1890ff;
$--color-success: #13ce66;
$--color-warning: #FFBA00;
$--color-danger: #ff4949;
// $--color-info: #1E1E1E;
$--button-font-weight: 400;
// $--color-text-regular: #1f2d3d;
$--border-color-light: #dfe4ed;
$--border-color-lighter: #e6ebf5;
$--table-border:1px solid #dfe6ec;
/* icon font path, required */
$--font-path: '~element-ui/lib/theme-chalk/fonts';
// @import "~element-ui/packages/theme-chalk/src/index";
:export {
theme: $--color-primary;
}
2.考虑到项目全局都有可能使用到颜色这个变量,所以决定用vuex,如图:新建文件 settings.js
settings.js 内容如下,theme 的初始值为 elementui-variables.scss 中定义的theme。
import variables from '@/styles/element-variables.scss'
const settings = {
state: {
theme: variables.theme,
},
mutations: {
CHANGE_SETTING: (state, { key, value }) => {
if (state.hasOwnProperty(key)) {
state[key] = value
}
}
},
actions: {
changeSetting({ commit }, data) {
commit('CHANGE_SETTING', data)
}
}
}
export default settings
ThemePicker文件内容如下,可以看到默认的颜色是 this.$store.state.settings.theme ,它是在 elementui-variables.scss 中定义的。
辅助文件:utils/color.js
import color from 'css-color-function'
import formula from './formula.json'
const generateColors = primary => {
let colors = {}
console.log("当前颜色:" , primary)
Object.keys(formula).forEach(key => {
const value = formula[key].replace(/primary/g, primary)
colors[key] = color.convert(value)
})
return colors
}
export default generateColors
页面上有些组件的样式可能是自定义的,不会随 theme 一起变化,可这么实现:
{{ tag.name }}
computed: {
//...,
theme() {
return this.$store.state.settings.theme
}
},
4.最后在页面上使用 ThemePicker 组件即可:
实际效果录屏:
引入步骤
1、访问网址https://www.iconfont.cn/
2、选择好后加入购物车
如何批量添加阿里巴巴iconfont图标到购物车
在页面上右键--审查元素,切换到console 控制台 输入如下代码,其实就是触发点击事件,可以一次性选择当前页面所有的图标
var span = document.querySelectorAll('.icon-cover');
for (var i = 0, len = span.length; i < len; i++) {
console.log(span[i].querySelector('span').click());
}
3、进入购物车,添加至项目
4、进入我的项目,下载到本地
5、将解压后的文件放入目标文件夹下
6、在main.js文件内全局引入 阿里云字体图标 css,记得使用 路径要正确,如果此时编译不正确,说明 引用路径存在问题
为避免出现图标方框的情况,请对font进行初始化
在reset.css 或者全局index.css中加入如下代码
// 阿里字体图标设置
.icon, .iconfont {
font-family:"iconfont" !important;
font-size:16px;
font-style:normal;
-webkit-font-smoothing: antialiased;
-webkit-text-stroke-width: 0.2px;
-moz-osx-font-smoothing: grayscale;
}
7、项目中使用字体图标,就可以使用了
(3)Symbol SVG图片格式
①支持多色图标了,不再受单色限制。
②支持像字体那样通过font-size,color来调整样式。
③支持 ie9+
④可利用CSS实现动画。
⑤减少HTTP请求。
⑥矢量,缩放不失真
⑦可以很精细的控制SVG图标的每一部分
使用方法:
第一步:拷贝下载项目,复制inconfont.js到资源文件目录下,并引入inonfont.js文件
引入 ./iconfont.js
第二步:加入通用css代码(引入一次就行):
第三步:挑选相应图标并获取类名,应用于页面:
引入过程中出现的问题:页面没有出现图标,原因:在main.js中没有引入iconfont.js
上图中的引入方式,在iconfonts.js中注册了标签
我的方式是在上面的基础上封装了一层,每次使用的时候可以不用写
我的目录结构:
icons/svg中放所有的图标文件
1)index.js中代码功能,注册全局标签,并获取所有的svg文件,index.js中的代码如下:
import Vue from 'vue'
import SvgIcon from '../../components/SvgIcon'// svg component
// register globally
Vue.component('svg-icon', SvgIcon)
const req = require.context('./svg', false, /\.svg$/)
const requireAll = requireContext => requireContext.keys().map(requireContext)
requireAll(req)
2)SvgIcon组件中的代码,主要共功能将引入图标的代码封装到组件中,并注册为全局标签使用
<template>
<div v-if="isExternal" :style="styleExternalIcon" class="svg-external-icon svg-icon" v-on="$listeners" />
<svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners">
<use :href="iconName" />
svg>
template>
<script>
// doc: https://panjiachen.github.io/vue-element-admin-site/feature/component/svg-icon.html#usage
import { isExternal } from '../utils/validate'
export default {
name: 'SvgIcon',
props: {
iconClass: {
type: String,
required: true
},
className: {
type: String,
default: ''
}
},
computed: {
isExternal() {
return isExternal(this.iconClass)
},
iconName() {
return `#icon-${this.iconClass}`
},
svgClass() {
if (this.className) {
return 'svg-icon ' + this.className
} else {
return 'svg-icon'
}
},
styleExternalIcon() {
return {
mask: `url(${this.iconClass}) no-repeat 50% 50%`,
'-webkit-mask': `url(${this.iconClass}) no-repeat 50% 50%`
}
}
}
}
script>
<style scoped>
.svg-icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
.svg-external-icon {
background-color: currentColor;
mask-size: cover!important;
display: inline-block;
}
style>
3)在main.js中引入即可
import './assets/icons'
import './assets/fonts/iconfont.js'
最近在用vue写pc端项目,用vuex来做全局的状态管理, 发现当刷新网页后,保存在vuex实例store里的数据会丢失。
1. 产生原因
2. 解决思路
3. 解决过程
1. 产生原因
其实很简单,因为store里的数据是保存在运行内存中的,当页面刷新时,页面会重新加载vue实例,store里面的数据就会被重新赋值。
2. 解决思路
一种是state里的数据全部是通过请求来触发action或mutation来改变
一种是将state里的数据保存一份到本地存储(localStorage、sessionStorage、cookie)中
很显然,第一种方案基本不可行,除非项目很小或者vuex存储的数据很少。而第二种可以保证刷新页面数据不丢失且易于读取。
3. 解决过程
首先得选择合适的客户端存储
localStorage是永久存储在本地,除非你主动去删除;
sessionStorage是存储到当前页面关闭为止;
cookie则根据你设置的有效时间来存储,但缺点是不能储存大数据且不易读取。
我选择的是sessionStorage,选择的原因vue是单页面应用,操作都是在一个页面跳转路由,另一个原因是sessionStorage可以保证打开页面时sessionStorage的数据为空,而如果是localStorage则会读取上一次打开页面的数据。
然后是怎么用sessionStorage来保存state里的数据。
第一种方案
由于state里的数据是响应式,所以sessionStorage存储也要跟随变化。又由于vuex规定所有state里数据必须通过mutation方法来修改,所以第一种方案就是mutation修改state的同时修改sessionStorage对应存储的属性
第二种方案
第一种方案确实可以解决问题,但这种方法很明显让人觉得怪异,都这样了,那不如直接用sessionStorage来做状态管理。
那怎么才能不用每次修改state时同时也要修改sessionStorage呢?这时我们可以换一个思路,因为我们是只有在刷新页面时才会丢失state里的数据,那有没有办法在点击页面刷新时先将state数据保存到sessionStorage,然后才真正刷新页面?
当然有,beforeunload这个事件在页面刷新时先触发的。那这个事件应该在哪里触发呢?我们总不能每个页面都监听这个事件,所以我选择放在app.vue这个入口组件中,这样就可以保证每次刷新页面都可以触发。
具体的代码如下:在入口App.vue下加入
描述:在jsp中vue的时候,页面加载的时候总会有{{ }}闪烁,看着太烦人了。
解决方案:
[v-cloak] {
display: none;
}
... 其它元素 ...
vue 中拼接代码
vue 中处理
此方法试用所有需要重置数据的场景
el-dialog打开一次之后,再次打开之前的数据不会销毁,依然存在。
我们需要在关闭后重新初始化数据。
this.$refs[formRef].resetFields();
复制代码
难道一个个的重新手动赋值吗?当然可以,就是比较麻烦。好在vue帮我们保存了一份原始数据,直接把data复制为原始数据即可
this.$data = this.$options.data();
以上就可以正常运行, 但是如果data中有表单验证相关,会导致控制台出现报错信息,如下面代码中的ruleValidate,排除即可。
data () {
return {
dialogVisible: false,
submitLoading: false,
model: {
id: 0,
carCard: "",
driver: "",
remark: "",
},
ruleValidate: {
carCard: {required: true, message: "不能为空", trigger: "blur"},
},
};
},
重置表单数据,使用的地方特别多,我们封装为全局方法
//重置表单,formRef为表单的ref值,excludeFields为要排除重新初始化值得属性
Vue.prototype.$reset = function (formRef, ...excludeFields) {
this.$refs[formRef].resetFields();
let obj1 = this.$data;
let obj2 = this.$options.data.call(this);
if (!excludeFields || excludeFields.length === 0) {
excludeFields = ["ruleValidate"];
}
for (let attrName in obj1) {
if (excludeFields && excludeFields.includes(attrName)) {
continue;
}
obj1[attrName] = obj2[attrName];
}
};
使用方法
取消
保存
A页面中调起B(dialog)页面,B(dialog)页面调起C(dialog)页面,会出现遮罩问题,如下图:
解决办法:在C页面中加入append-to-body,如下图: