前言
vue真的很強(qiáng)大, 用過它的人都說好
SPA(single page app, 即單頁(yè)面應(yīng)用)
該文介紹了spa項(xiàng)目的創(chuàng)建, 設(shè)置路由, 添加組件, 使用markdown編輯器, 使用webpack實(shí)現(xiàn)按需加載, 使用sass等幾部分的內(nèi)容, 內(nèi)容較多, 慎入
正文
項(xiàng)目演示地址: 戳這里
項(xiàng)目代碼地址: 戳這里
項(xiàng)目準(zhǔn)備
- 初始化工程
npm install vue-cli -g #install vue-cli
vue init webpack-sample my-vue-webpack-simple # 創(chuàng)建基于模板webpack-sample的vue項(xiàng)目
- 安裝依賴
# install dependencies
npm install
- 開啟服務(wù)
# 開啟本地服務(wù)器, 自帶熱加載, 默認(rèn)地址localhost:8080
npm run dev
# 構(gòu)建生產(chǎn)環(huán)境代碼
npm run build
使用vue-router
- 安裝
npm install vue-router --save
- 配置router
./src/main.js文件:
import Vue from 'vue'
import VueRouter from 'vue-router'
//components lists
import App from './App.vue';
import Home from './components/home.vue';
const Foo = { template: '<div>foo</div>' };
const Bar = { template: '<div>bar</div>' };
//創(chuàng)建子類構(gòu)造器
const app = Vue.extend(App);
//使用模塊化機(jī)制編程, 要調(diào)用 Vue.use(VueRouter)
Vue.use(VueRouter);
//定義路由
const routes = [
{ path: '/', component: Home },
{ path: '/foo', component: Foo },
{ path: '/bar', component: Bar }
];
//創(chuàng)建 router 實(shí)例,然后傳 `routes` 配置
const router = new VueRouter({
routes
});
// 掛載到根實(shí)例
new app({
router
}).$mount('#app');
- 設(shè)置路由模板
./src/App.vue文件:
<template>
<div class="main">
<transition name="fade" mode="out-in" appear>
<keep-alive>
<router-view></router-view>
</keep-alive>
</transition>
</div>
</template>
- 添加頁(yè)面過渡css(vue2.0和1.0有些許差別, 詳情看文檔)
.fade-enter-active, .fade-leave-active {
transition: opacity 0.3s ease;
}
.fade-enter, .fade-leave-active {
opacity: 0;
}
添加組件
- 改寫
home.vue
<template>
<div id="home">
<h1>{{ msg }}</h1>
<ol>
<todo-item v-for="todo in todos" v-bind:todo="todo"></todo-item>
</ol>
<my_footer></my_footer>
</div>
</template>
<script>
//定義組件
import my_footer from './footer.vue'
export default {
name: 'home',
data () {
return {
msg: 'Welcome to Your Vue.js App',
todos: [
{ text: 'Learn JavaScript' },
{ text: 'Learn Vue' },
{ text: 'Build something awesome' }
]
}
},
components:{ //引入組件
'my_footer': my_footer,
'todo-item': {
props: ['todo'],
template: '<li>{{ todo.text }}</li>'
}
}
}
</script>
- 添加
footer.vue
<template>
<div class="weui-footer">
<p class="weui-footer__links">
<a href="javascript:void(0);" class="weui-footer__link">底部鏈接</a>
<a href="javascript:void(0);" class="weui-footer__link">底部鏈接</a>
</p>
<p class="weui-footer__text">Copyright ? 2008-2016 weui.io</p>
</div>
</template>
使用簡(jiǎn)單的markdown編輯器
- 引入官網(wǎng)marked文件
import marked from '../../statics/js/marked@0.3.6.js'
- 使用marked方法來計(jì)算輸入的內(nèi)容
<template>
<div id="editor">
<textarea :value="input" v-model="input"></textarea>
<div class="weui-panel">
<div class="weui-panel__hd">markdown預(yù)覽</div>
<div class="box" v-html="compiledMarkdown"></div>
</div>
</div>
</template>
<script>
import marked from '../../statics/js/marked@0.3.6.js'
export default{
name:'editor',
data(){
return {
input: '# input markdown'
}
},
computed: {
compiledMarkdown: function () {
return marked(this.input, {
sanitize: true
})
}
}
}
</script>
<style>
# balabala 省略
</style>
- 給代碼加高亮
引入 highlight 的 js 和 css , 再在 marked 方法中配置下就OK了, 以下給出增加的代碼片段, 插到相應(yīng)位置上就可以了
<template>
<div id="editor">
<link rel="stylesheet" href="../../statics/css/solarized_light.min.css">
<!-- ... -->
</div>
</template>
<script>
//...
import hljs from '../../statics/js/highlight.min'
//...
return marked(this.input, {
sanitize: true,
highlight: function(code, lang) {
return hljs.highlightAuto(code, [lang]).value;
}
})
//...
</script>
- 更多的設(shè)置自行g(shù)oogle
源碼中默認(rèn)的配置參數(shù):
marked.defaults = {
gfm: true,
tables: true,
breaks: false,
pedantic: false,
sanitize: false,
sanitizer: null,
mangle: true,
smartLists: false,
silent: false,
highlight: null,
langPrefix: 'lang-',
smartypants: false,
headerPrefix: '',
renderer: new Renderer,
xhtml: false
};
vue webpack 按需加載的實(shí)現(xiàn)
我們知道在這之前,使用vue webpack 模板構(gòu)建出的項(xiàng)目是將所有的js都編譯到一個(gè)build.js文件中,旨在只加載一次資源,后續(xù)會(huì)有本地化app的效果,但當(dāng)業(yè)務(wù)邏輯較多,組件較多時(shí),這個(gè)文件就會(huì)很大,從而使首屏加載的時(shí)間very very long ~,第一次進(jìn)入項(xiàng)目就要等上半分乃至幾分鐘,要是我的話肯定等不鳥→_→;
那么讀到這里,有點(diǎn)經(jīng)驗(yàn)的肯定會(huì)想到,能不能優(yōu)化成類似requirejs的按需加載,首屏不需要的資源先不加載,到使用時(shí)再下載下來;官網(wǎng)的東西還是很強(qiáng)大的,雖然目前國(guó)內(nèi)還沒什么現(xiàn)成的例子,但通過官方文檔,還是能進(jìn)展一二的。廢話不多說了,下面重點(diǎn)來了
- 更改
webpack.config.js文件,使用webpack的chunk 查看CommonChunks插件
//...
//初始化webpack 自帶的 chunk 插件
var commonsPlugin = new webpack.optimize.CommonsChunkPlugin('common');
module.exports = {
entry: './src/main.js', //可引入多個(gè)入口文件,編譯后的文件個(gè)數(shù)也會(huì)相應(yīng)增多
output: {
path: path.resolve(__dirname, './dist'),
publicPath: 'dist/',
filename: '[name].js', //自動(dòng)生成文件名
chunkFilename: "chunts/[name]-[chunkhash:8].js" //生成的子文件路勁/文件名
},
//...
plugins: [ commonsPlugin ],
//...
}
//...
- 修改入口文件
src/main.js
之前的路由寫法:
import home from './components/home.vue';
import lists from './components/lists.vue';
const routes = [
{ path: '/', component: Index },
{ path: '/home', component: home },
{ path: '/lists', component: lists }
];
修改之后:點(diǎn)擊查看官網(wǎng)-異步組件模塊
const routes = [
{
path: '/',
component: function (resolve) {
require(['./components/index.vue'], resolve)
}
},
{
path: '/home',
component: function (resolve) {
require(['./components/home.vue'], resolve)
}
},
{
path: '/lists',
component: function (resolve) {
require(['./components/lists.vue'], resolve)
}
}
];
或者改寫成這樣:(是不是更加一目了然呢??)
const routes = [
{ path: '/', component: view('index') },
{ path: '/home', component: view('home') },
{ path: '/lists', component: view('lists') }
];
//rebase url `./components/`
function view(name) {
return function(resolve) {
require(['./components/' + name + '.vue'], resolve);
}
};
- 在
index.html中替換依賴文件build.js
<script src="./dist/common.js"></script>
<script src="./dist/main.js"></script>
- 執(zhí)行
npm run build,編譯后的文件結(jié)構(gòu)如下
├── dist/ # 編譯后的目標(biāo)文件夾
│ ├── common.js # js分發(fā)文件
│ ├── main.js # 壓縮處理后的入口文件
│ └── chunts # 按需加載的子文件夾
│ ├── 0-xxxxxx.js
│ ├── 1-xxxxxx.js
│ ├── 2-xxxxxx.js
│ └── ...
├── ...
如圖:
<p style="text-align: center;">
<img src="http://statics.oulafen.com/blog_vue-webpack-chunts.jpg" style="margin: auto; width: 50%;" />
</p>
切換路由效果
- 應(yīng)用場(chǎng)景:同級(jí)路由間切換用
fade動(dòng)效,不同級(jí)路由間切換時(shí),用slide-left和slide-right - 修改
src/App.vue文件
<template>
<div class="main">
<transition :name="routerTransition" mode="out-in" appear>
<keep-alive>
<router-view></router-view>
</keep-alive>
</transition>
</div>
</template>
<script>
export default{
data(){
return {
routerTransition: 'fade'
}
},
watch: {
'$route' (to, from) {
const toDepth = to.path.split('/').length
const fromDepth = from.path.split('/').length
if(toDepth != fromDepth){
this.routerTransition = toDepth < fromDepth ? 'slide-right' : 'slide-left'
}else{
this.routerTransition = 'fade'
}
}
}
}
</script>
- 添加動(dòng)效css
.fade-enter-active, .fade-leave-active {
transition: opacity 0.3s ease;
}
.fade-enter, .fade-leave-active {
opacity: 0;
}
.slide-left-enter, .slide-right-leave-active {
opacity: 0;
-webkit-transform: translate(30px, 0);
transform: translate(30px, 0);
}
.slide-left-leave-active, .slide-right-enter {
opacity: 0;
-webkit-transform: translate(-30px, 0);
transform: translate(-30px, 0);
}
.slide-left-enter-active, .slide-left-leave-active, .slide-right-enter-active, .slide-right-leave-active{
transition: all .3s cubic-bezier(.55,0,.1,1);
}
效果圖如下:
<p style="text-align: center;">
<img src="http://statics.oulafen.com/blog_vue_spa_demo_index_2.gif" style="margin: auto;" />
</p>
使用scss
- 安裝依賴
npm install sass-loader node-sass --save-dev
- 配置webpack config
module.exports = {
...
module: {
rules: [
...,
{
test: /\.scss$/,
loaders: ["style-loader", "css-loader", "sass-loader"]
}
]
}
};
- 在模塊中引入scss文件
require("../statics/css/style.scss");
參考鏈接:https://github.com/jtangelder/sass-loader
問題總結(jié)
- build項(xiàng)目之后, 靜態(tài)資源的如圖片加載失敗
原因肯定是路徑不對(duì), 所以才找不到, 建議將webpack.config.js中的publicPath改為路徑dist/, 若不行,再查看其它有關(guān)路徑設(shè)置的地方