文章很长,希望能给vue初学者一些小小的帮助。
一. 搭建一个完整的vue项目
1. 安装node环境
① 下载地址为:https://nodejs.org/en/
② 检查是否安装成功:cmd输入命令 node -v 或者 npm -v, 如果输出版本号,说明我们安装node环境成功。
2. 为了提高我们的效率,可以使用淘宝的镜像:http://npm.taobao.org/(可省略)
① 输入:npm install -g cnpm –registry=https://registry.npm.taobao.org,即可安装npm镜像,以后再用到npm的地方直接用cnpm来代替就好了。
② 检查是否安装成功:cmd输入命令 cnpm -v ,不报错即为安装成功。
( 如果此处安装淘宝的镜像,下面命令的 npm 都由 cnpm 代替 )
3. 搭建vue项目环境
① 全局安装vue-cli npm install --global vue-cli
② 进入你的项目目录,创建一个基于 webpack 模板的新项目: vue init webpack 项目名
后面将会有问题,需要确认敲回车,具体命令及意思如下所示:
Vue build ==> 打包方式,回车即可;
Install vue-router ==> 是否要安装 vue-router,项目中肯定要使用到 所以Y 回车;
Use ESLint to lint your code ==> 是否需要 js 语法检测 目前我们不需要 所以 n 回车;
Set up unit tests ==> 是否安装 单元测试工具 目前我们不需要 所以 n 回车;
Setup e2e tests with Nightwatch ==> 是否需要 端到端测试工具 目前我们不需要 所以 n 回车;
4. 进入项目:cd 项目名,安装依赖 npm i
安装成功后,项目文件夹中会多出一个目录: node_modules
5. npm run dev 或者 npm run start,启动项目
二. vue项目目录讲解
1、build:构建脚本目录
1)build.js ==> 生产环境构建脚本;
2)check-versions.js ==> 检查npm,node.js版本;
3)utils.js ==> 构建相关工具方法;
4)vue-loader.conf.js ==> 配置了css加载器以及编译css之后自动添加前缀;
5)webpack.base.conf.js ==> webpack基本配置;
6)webpack.dev.conf.js ==> webpack开发环境配置;
7)webpack.prod.conf.js ==> webpack生产环境配置;
2、config:项目配置
1)dev.env.js ==> 开发环境变量;
2)index.js ==> 项目配置文件;
3)prod.env.js ==> 生产环境变量;
3、node_modules:npm 加载的项目依赖模块
4、src:这里是我们要开发的目录,基本上要做的事情都在这个目录里。里面包含了几个目录及文件:
1)assets:资源目录,放置一些图片或者公共js、公共css。这里的资源会被webpack构建;
2)components:组件目录,我们写的组件就放在这个目录里面;
3)router:前端路由,我们需要配置的路由路径写在index.js里面;
4)App.vue:根组件;
5)main.js:入口js文件;
5、static:静态资源目录,如图片、字体等。不会被webpack构建
6、index.html:首页入口文件,可以添加一些 meta 信息等
7、package.json:npm包配置文件,定义了项目的npm脚本,依赖包等信息
8、README.md:项目的说明文档,markdown 格式
9、.xxxx文件:这些是一些配置文件,包括语法配置,git配置等
三. Webstorm下配置node环境
全局安装完node,会发现Webstorm的命令行无法使用node命令,这时候就需要在Webstorm里配置node环境。
1. File -> Settings->?(node.js)->Node.js and NPM
2. Node interpreter -> ? -> Add... -> Add Local -> node.js的绝对路径,如下图:
如果仍然不可以,记得重启一下你的Webstorm哦!
注:定位到安装目录后不做任何操作Package不会有那么多内容
四. vue中如何使用less
① 安装less依赖,npm install less less-loader --save
② 修改 webpack.base.conf.js 文件,配置 loader 加载依赖,让其支持外部的less,在原来的代码上添加
module: {
rules: [
{
test: /\.less$/,
loader: "style-loader!css-loader!less-loader",
},
]
},
现在基本上已经安装完成了,然后在使用的时候在style标签里加上lang=”less”里面就可以写less的代码了(style标签里加上 scoped 为只在此作用域 有效)。或者
@import './index.less'; //引入全局less文件
html中直接引入:
<link rel="stylesheet/less" type="text/css" href="文件路径/styles.less">
<script src="文件路径/less.js" type="text/javascript"></script>
五. vue移动端适配插件lib-flexible
① 安装插件 lib-flexible:npm i lib-flexible --save-dev
② 在main.js中引入lib-flexible:import 'lib-flexible/flexible'
css 可将单位转换为 rem:
<style lang="less" scoped>
@wid : 75;
.home{
width: 100vw;
height: 100vh;
}
.home .top{
width: 100vw;
height: 537rem/@wid;
background: url("../assets/home1.png") no-repeat;
-webkit-background-size: 100% 100%;
background-size: 100% 100%;
}
</style>
六. vue项目中 import '@/components/order' 中 @ 是什么意思
大家在写vue项目时是否会经常遇到类似 import order from '@/components/order' 的代码?有没有和我一样的疑虑:@ 到底指代什么呢?
@ 是自行配置的一个 alias,默认是从 src 目录开始查找,也可以自己配置一些其它路径或者符号,根目录的别名,可以自行在 webpack.base.conf.js 里面的 alias 去设置:
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src'),
}
},
七. 自定义border的长度 样式 -- after 伪类
不论是vue项目还是原生,我们有时会遇到需要更改一个边框的多种样式,就像这种情况:
这是一个我自己手写的步骤条,他需要虚线变成实线,而且是一定长度,那我们怎么办呢?使用 after伪类 创建了一个有长度的右边框,通过定位将虚线框遮盖起来,从而达到效果。
.left{
width: 69rem/@wid;
border-right: 2px #B5B4B4 dashed;
position: relative;
&:after {
content: '';
position: absolute;
top: 0;
left: 69rem/@wid;
/*改变高度决定步骤条的颜色变化*/
height: 122rem/@wid;
border-right: 2px solid #B5B4B4;
}
}
拓展:css中的伪类
css3为了区分伪类和伪元素,伪元素采用双冒号写法。
常见伪类——:hover,:link,:active,:target,:not(),:focus。
常见伪元素——::first-letter,::first-line,::before,::after,::selection。
::before和::after下特有的content,用于在css渲染中向元素逻辑上的头部或尾部添加内容。
这些添加不会出现在DOM中,不会改变文档内容,不可复制,仅仅是在css渲染层加入。所以不要用:before或:after展示有实际意义的内容,尽量使用它们显示修饰性内容,例如图标。
(::before和::after必须配合content属性来使用,content用来定义插入的内容,content必须有值,至少是空。默认情况下,伪类元素的display是默认值inline,可以通过设置display:block来改变其显示。)
举例:网站有些联系电话,希望在它们前加一个icon☎,就可以使用:before伪元素,如下:
<!DOCTYPE html>
<meta charset="utf-8" />
<style type="text/css">
.phoneNumber::before {
content:'\260E';
font-size: 15px;
}
</style>
<p class="phoneNumber">12345645654</p>
// ☎12345645654
详细参考博客:https://www.cnblogs.com/mycoke/p/6056187.html
八. vue发起网络请求 -- 封装
( 如果不想要封装请去参考博客:https://www.kancloud.cn/wsj-/vue/645840 )
接口是这样的:
1. 进入根目录,安装 axios 模块
npm install axios -S
2. 封装请求。新建 http.js 文件,并在 mian.js 中引入。
请求的 url 与终端类型 不同时需要在 http.js 文件中修改 网络请求地址 与 终端类型。
import Vue from 'vue';
import axios from 'axios';
Vue.config.baseHttpUrl = 'https://dongjiang.ykelai.com/bcrm/admin.php'; //网络请求地址
Vue.config.platform = 'merchandiser'; //终端类型
// axios 配置
axios.defaults.timeout = 10000; //网络请求超时时间
axios.defaults.baseURL = Vue.config.baseHttpUrl;
axios.defaults.headers.post['Content-Type'] = 'application/json;charset=utf-8';
// http request 拦截器
axios.interceptors.request.use(
config => {
config.headers = {'Content-Type': 'application/json;charset=utf-8','Access-Control-Allow-Origin':'*'}
return config;
},
err => {
return Promise.reject(err);
});
Vue.prototype.axios = axios;
Vue.prototype.post = function(obj){
const _this = this;
obj.data.platform = Vue.config.platform;
axios.post('',obj.data)
.then(function (response) {
try{
if(obj.success && typeof obj.success == 'function'){
if(response.data.code == 999){
_this.$router.replace({path:'/'})
}else{
obj.success(response.data)
}
}else{
throw "function 'success' undefind or it's not a function: This HttpRequest has no 'success' methods"
}
}
catch(err){
console.log('err',err);
}
})
.catch(function (error) {
if(obj.fail && typeof obj.fail == 'function'){
obj.fail(error)
}else{
obj.fail = function(error){
console.log('error',error);
alert('网络请求错误');
}
}
});
}
引入 main.js :
import http from './http/http'
3. 发起请求(样式代码就不放了)
<template>
<!--登录注册-->
<div class="regist">
<div class="reg">
<h2>你好,欢迎登陆</h2>
<div class="phone">
<img src="../assets/reg1.png" alt="">
<input type="text" placeholder="请输入手机号码" v-model="user">
</div>
<div class="passwd">
<div class="left">
<img src="../assets/reg2.png" alt="">
<input :type="passwd" placeholder="请输入密码" v-model="pass">
</div>
// 三目运算符的使用,控制密码是显示还是小圆点
<img src="../assets/reg3.png" alt="" class="right" @click="passwd = passwd == 'password'? 'text': 'password'">
</div>
<div class="btn" @click="userRegist()">登 录</div>
<div class="handle">
<div>
<span>账号注册</span>
<img src="../assets/right2.png" alt="">
</div>
<div>
<span>忘记密码</span>
<img src="../assets/right2.png" alt="">
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "regist",
data(){
return{
passwd: 'password',
user: '',
pass: ''
}
},
methods:{
userRegist:function () {
this.post({
data:{
request: 'public.auth.login_from_password',
username: this.user,
password: this.pass
},
success: res =>{
console.log(res);
}
})
},
}
}
</script>
九. vue项目去掉地址栏中的 # 及 打包编译后遇到的问题
vue项目运行后url都会有一个 # 号,那如何去掉 # 呢?可以使用 history模式,只要在router->index.js加一行代码。代码如下:
export default new Router({
mode: 'history',
})
是不是很简单呢,但是它也有它的问题。使用这个模式,在开发阶段一切都是正常的,可是打包之后,访问项目会发现页面一片空白的情况,解决办法如下:
A:很多项目都放在了服务器根目录下面,访问后的url就是:http://123.com。
但是我们经常都会新建目录,此时的 url 就会变成 http://123.com/nice/app。那怎么办呢?
B:假如我的打开地址是:http://123.com/nice/app,配置了模块,或者放置在了子文件夹下面,那么就会出问题。其实,这是因为router无法找到路径中的组件,所以也就无法渲染了。只需要修改router中的index.js,加一个基础路径就可以了。
export default new Router({
mode: 'history',
base: '/nice/app/'
})
十. vue项目按需加载路由 and vue 引入本地js
大家都知道在router->index.js里这样写:
import Home from '@/components/Home'
export default new Router({
routes: [
{
path : ‘/’, //到时候地址栏会显示的路径
name : ‘Home’,
component : Home // Home是组件的名字,这个路由对应跳转到的组件。。注意component没有加“s”.
},
})
但是这样写是一开始所有路由会全部加载,那么我们如何按需加载路由呢,代码如下:
// router->index.js
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router);
export default new Router({
routes:[
{path: '/', name: 'regist', component: (resolve) => require(['../components/regist'], resolve)},
{path: '/home', name: 'home', component: (resolve) => require(['../components/home'], resolve)}
]
})
vue 项目引入本地 js文件 需要在项目下的 index.html 文件里的 body 加如下代码:
<script src="./static/js/jweixin-1.0.0.js"></script>
十一. router-link 与 v-for 使用的坑
<div id="app">
<router-link v-for="item in items" to="'/' + item.id"
:key="item.id">{{item.content}}</router-link>
</div>
结果发现router-view并没有渲染。并且我发现把router-link里面的to写死,即to="/home"或者to="/overview"就能够渲染,但这并不是我想得到的效果。查阅官方文档后得到解决方案。
首先,我们需要把 to 改成 :to 即 v-bind:to 的缩写,然后可以根据 path 或 name 来进行设置。下面给出代码示例:
<router-link v-for="item in items" :to="{path: '/' + item.id}"
:key="item.id">{{item.content}}</router-link>
<router-link v-for="item in items" :to="{path: item.id}"
:key="item.id">{{item.content}}</router-link>
<router-link v-for="item in items" :to="{name: item.id}"
:key="item.id">{{item.content}}</router-link>
十二. vue中使用go()和back()两种返回上一页的区别
vue 项目我们经常需要用到返回上一页的操作,经常用的是 go( )和 back() 方法,那么这两种方法有什么区别呢:
history.go(-1):后退+刷新;
history.go(1) :前进
history.back():后退 ;
history.back(0) 刷新;
history.back(1):前进
十三. vue跳转的两种方法
<router-link to='two'><button>点我到第二个页面</button></router-link>
html :
<button @click="hreftwo" class="test-one">点我到第二个页面</button>
js :
methods:{
//跳转页面
hreftwo(){
this.$router.push({path:'/two'})
}
}
十四. vue.js实现选项卡的切换效果
vue 项目中我们经常需要用到选项卡的效果,vue非常灵活,方法不止这一种,我给的示例代码,是我认为最好理解的方法,有兴趣的可以去度娘搜一下哦,话不多说,上代码。
html:
<div id="app">
<div class="tab">
<a href="#" @click="tabId=0" class="{tabId=0}">前端框架</a>
<a href="#" @click="tabId=1" class="{tabId=1}">Vue</a>
<a href="#" @click="tabId=2" class="{tabId=2}">Angular</a>
<a href="#" @click="tabId=3" class="{tabId=3}">React</a>
</div>
<div class="box">
<div v-show="tabId===0">111</div>
<div v-show="tabId===1">222</div>
<div v-show="tabId===2">333</div>
<div v-show="tabId===3">444</div>
</div>
</div>
css:
<style>
*{
margin: 0;
padding: 0;
}
#app{
width: 800px;
margin: 0 auto;
}
a{
text-decoration: none;
height: 50px;
width: 25%;
background-color: lightblue;
float: left;
line-height: 50px;
text-align: center;
}
.box{
border: 1px solid #ccc;
height: 400px;
}
</style>
js:
<script src="node_modules/vue/dist/vue.min.js"></script>
<script>
new Vue ({
el:'#app',
data:{
tabId:1
}
})
</script>
此方法通过变量 tabId 的值的判断来显示不同的内容并进行选项卡的样式切换,代码少,浅显易懂。
十五. vue h5移动端禁止缩放
vue 移动端项目在连续点击时,有时候会出现页面的缩放,这时我们需要禁止页面的缩放,具体代码:
1. 安卓:项目目录 -> index.html
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
2. ios:项目目录 -> src -> App.vue
<script>
window.onload = function() {
document.addEventListener('touchstart', function(event) {
if (event.touches.length > 1) {
event.preventDefault()
}
})
document.addEventListener('gesturestart', function(event) {
event.preventDefault()
})
}
</script>
十六. vuex下state数据的存储
vuex是什么我之前也有写过,忘记的小伙伴们记得去看哦:生南星->全栈随笔->三大框架-> VueX详解。今天具体说一下 vuex 下 state 数据的存取。
1. vuex 下 state 数据的存储:
a. 项目目录 -> 新建store 目录 -> 新建 store.js 文件
import vuex from 'vuex'
import Vue from 'vue'
Vue.use(vuex)
export default new vuex.Store({
/*state必须*/
state:{
user:{}
},
mutations:{
setUser(state,v){
state.user=v;
}
}
})
b. 项目入口文件: 项目目录 -> src -> main.js 加如下代码:
import store from './store/store'
new Vue({
el: '#app',
router,
// 我是需要加的那句代码-----
store:store,
template: '<App/>',
components: { App }
})
c. 使用页面的数据存储
methods: {
loginUser () {
this.$store.commit('setUser',this.user)
}
}
注:this.$store.commit('setUser',this.user) 中的 setUser 要和 store.js 文件中的 mutations 中的方法相同。setUser方法中的 v 即是页面中想要存储的 this.user
2. 页面取 vuex 中的数据
a. 在插值表达式中用 $store.state.count 来获取
<div>{{$store.state.count}}</div>
以下三种方法,都需要依赖vue的computed计算属性来实现。
因为:在vue的实际应用中,computed属性可以在输出前,对data中的值进行改变,因此,我们就是利用computed的这一特性,在组件中获取vuex的state对象中的属性。
b. 在computed中定义一个方法,并return出 state 对象中的属性及其状态:
<div>{{count}}</div>
computed:{
count(){
return this.$store.state.count;
}
}
这里需要说明的是:因为store之前已经在mian.js中进行了全局引入,因此,在vue的组件中,就可以使用 this.$store 来调用,其中this代表Vue的实例(不可省略)。
c. 利用vuex的mapState方法来获取vuex的state对象中属性,它有两种写法:
1). (mapState中用对象的形式获取):
首先在组件中引入vuex的mapState方法, 然后在computed中这样写:
<div>{{count}}</div>
import { mapState } from 'vuex'
computed:{
...mapState({
count:state => state.count //使用ES6的箭头函数来给count赋值
})
}
2). (mapState中用数组的形式获取):
同样,需要先在组件中引入vuex的mapState方法, 然后在computed中这样写:
<div>{{count}}</div>
import { mapState } from 'vuex'
computed:{
...mapState(['count'])
}
十七. vue项目打包(解决背景图片不显示问题)
vue项目打包总是能遇到这样那样的问题,最常见的问题莫过于图片不显示,我之前写过详细的vue项目打包步骤及部署到服务器上的步骤,感兴趣的朋友可以去看一下,生南星->全栈随笔->三大框架->vue项目简书(三)--打包部署就可以查看喽(或者后台直接回复:vue打包部署
),好啦,言归正传,背景图片显示不出来该怎么办呢?
1. 将绝对路径改为相对路径。目标文件:项目目录 > config文件夹 > index.js
build:{
assetsPublicPath:'/' 改为:assetsPublicPath:'./' (加一个点变为相对路径)
}
2. 配置背景图片路径 -- 打包后的css文件夹内app.css文件访问static/img/’图片名’路径错误访问不到图片,在build->utils.js 中配置路径。目标文件:项目目录 > build文件夹 >utils.js. 在下面所示位置添加代码 publicPath:'../../',
// Extract CSS when that option is specified
// (which is the case during production build)
if (options.extract) {
return ExtractTextPlugin.extract({
use: loaders,
// 我是刚被添加额那句代码
publicPath:'../../',
fallback: 'vue-style-loader'
})
} else {
return ['vue-style-loader'].concat(loaders)
}
}
十八. input text 设置 disabled 单击事件不响应,怎么办?
将 disabled 换成 readonly 就好了。
拓展:
只读字段是不能修改的。不过,用户仍然可以使用 tab 键切换到该字段,还可以选中或拷贝其文本。
readonly 属性可以防止用户对值进行修改,直到满足某些条件为止(比如选中了一个复选框)。然后,需要使用 JavaScript 消除 readonly 值,将输入字段切换到可编辑状态。
十九. vue 项目 storage 本地存储
1. 封装:项目 -> src -> 新建目录 utils -> 新建 storage.js 文件
var u = {};
var isAndroid = (/android/gi).test(navigator.appVersion);
var uzStorage = function(){
var ls = window.localStorage;
if(isAndroid&&typeof os != 'undefined'){
ls = os.localStorage();
}
return ls;
};
export default {
setStorage:function(key, value){
if(arguments.length === 2){
var v = value;
if(typeof v == 'object'){
v = JSON.stringify(v);
v = 'obj-'+ v;
}else{
v = 'str-'+ v;
}
var ls = uzStorage();
if(ls){
ls.setItem(key, v);
}
}
},
getStorage:function(key){
var ls = uzStorage();
if(ls){
var v = ls.getItem(key);
if(!v){return;}
if(v.indexOf('obj-') === 0){
v = v.slice(4);
return JSON.parse(v);
}else if(v.indexOf('str-') === 0){
return v.slice(4);
}
}
},
rmStorage:function(key){
var ls = uzStorage();
if(ls && key){
ls.removeItem(key);
}
},
clearStorage:function(){
var ls = uzStorage();
if(ls){
ls.clear();
}
}
}
// console.log(u)
2. 在需要存储数据的页面调用 setStorage() 方法, 例如,登录页存储 token:
methods:{
userRegist:function () {
this.post({
data:{
request: 'public.auth.login_from_password',
username: this.user,
password: this.pass
},
success: res =>{
if(res.code === 0){
this.$router.push({path:'/home'});
this.$storage.setStorage('token',res.token)
}else {
alert(res.msg || '网络错误!');
}
}
})
},
}
3. 在需要取数据的页面调用 getStorage() 方法,例如其他页面需要向后台发送 token 请求数据
methods:{
searchOrder:function () {
this.post({
data:{
request: 'private.purchase.get_purchase_order_list',
token: this.$storage.getStorage('token'),
},
success: res => {}
})
},
}
注:storage.js 文件 中的 rmStorage() 方法用来移除已经存储的但是不需要的数据;clearStorage() 方法用来清除本地存储的所有数据。
文章到这里大概就结束了,最后再送一句我最近看到的话吧:成功不是将来才有的,而是从决定去做的那一刻起,持续累积而成。