閱讀此文檔,需對(duì) Laravel,VueJS 2,Webpack 有了解。
前端擴(kuò)展,指的是,針對(duì)項(xiàng)目 notadd/administration 的前端部分進(jìn)行擴(kuò)展功能的開(kāi)發(fā)。
完整示例,請(qǐng)參考模塊項(xiàng)目 notadd/content 。
前端擴(kuò)展包含的功能注入點(diǎn)如下:
項(xiàng)目 notadd/administration 的前端部分,是基于 VueJS 2 實(shí)現(xiàn)的單頁(yè)應(yīng)用(SPA)。
所以,對(duì)前端進(jìn)行擴(kuò)展,實(shí)際是對(duì) VueJS 項(xiàng)目的擴(kuò)展。
由于 VueJS 項(xiàng)目基于 Webpack 進(jìn)行構(gòu)建和打包,所以前端擴(kuò)展項(xiàng)目也必須基于 Webpack 進(jìn)行構(gòu)建和打包。
如何創(chuàng)建和開(kāi)發(fā) VueJS 2 的項(xiàng)目,請(qǐng)參見(jiàn) VueJS 官方文檔。
但是,Notadd 的前端擴(kuò)展項(xiàng)目,并不是一個(gè)完整的 VueJS 2 的項(xiàng)目,因?yàn)?Notadd 只接受 UMD 模塊風(fēng)格的前端模塊注入,所以在使用 Webpack 進(jìn)行模塊構(gòu)建時(shí),webpackConfig 中需要針對(duì) output 參數(shù)進(jìn)行調(diào)整,主要體現(xiàn):
配置代碼參考如下(來(lái)自文件 build/webpack.prod.conf.js):
var path = require('path')
var utils = require('./utils')
var webpack = require('webpack')
var config = require('../config')
var merge = require('webpack-merge')
var baseWebpackConfig = require('./webpack.base.conf')
var HtmlWebpackPlugin = require('html-webpack-plugin')
var ExtractTextPlugin = require('extract-text-webpack-plugin')
var OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
var env = config.build.env
var webpackConfig = merge(baseWebpackConfig, {
module: {
rules: utils.styleLoaders({
sourceMap: config.build.productionSourceMap,
extract: true
})
},
output: {
path: config.build.assetsRoot,
filename: utils.assetsPath('js/extension.js'),
library: 'notadd/content', // 必須定義 library 別名
libraryTarget: "umd" // 必須定義 libraryTarget 為 umd
},
plugins: [
new webpack.DefinePlugin({
'process.env': env
}),
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
}),
new ExtractTextPlugin({
filename: utils.assetsPath('css/[name].css')
}),
new OptimizeCSSPlugin()
]
})
if (config.build.productionGzip) {
var CompressionWebpackPlugin = require('compression-webpack-plugin')
webpackConfig.plugins.push(
new CompressionWebpackPlugin({
asset: '[path].gz[query]',
algorithm: 'gzip',
test: new RegExp(
'\\.(' +
config.build.productionGzipExtensions.join('|') +
')$'
),
threshold: 10240,
minRatio: 0.8
})
)
}
if (config.build.bundleAnalyzerReport) {
var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}
module.exports = webpackConfig
使用 Webpack 導(dǎo)出 UMD 模塊風(fēng)格的模塊是,在 Webpack 配置中定義的 entry 入口文件中,必須使用默認(rèn)導(dǎo)出模塊,作為 Notadd 前端功能注入的注入點(diǎn)。
代碼參考如下:
```ecmascript 6 import {headerMixin, installMixin, routerMixin} from './helpers/mixes'
let Core = {}
headerMixin(Core) installMixin(Core) routerMixin(Core)
export default Core
如上代碼所示,模塊 Core 即為項(xiàng)目構(gòu)建后的默認(rèn)導(dǎo)出模塊,在該示例中,使用了 mixin 特性,為模塊增加 header,install,router的注入邏輯。
##擴(kuò)展安裝注入
如上(默認(rèn)導(dǎo)出模塊)述說(shuō),installMixin 為模塊 Core 注入了 Core.install 的實(shí)現(xiàn),具體代碼如下:
```ecmascript 6
export function installMixin (Core) {
Core.install = function (Vue, Notadd) {
Core.instance = Notadd
vueMixin(Core, Vue)
}
}
export function vueMixin (Core, Vue) {
Core.http = Vue.http
}
Core.install 的調(diào)用者,為該方法提供了兩個(gè)對(duì)象,一個(gè)是 Vue 全局對(duì)象,一個(gè)是 Notadd 全局對(duì)象。
Vue 全局對(duì)象提供的特性,可以參考 VueJS 2 的官方文檔。
Notadd 全局對(duì)象主要包含如下特性:
所以,如果模塊 Core 中需要使用 Vue 或 Notadd 的任意對(duì)象,均可通過(guò) mixin 特性來(lái)附加。
如上(默認(rèn)導(dǎo)出模塊)述說(shuō),headerMixin 為模塊 Core 注入了 Core.header 的實(shí)現(xiàn),具體代碼如下:
```ecmascript 6 export function headerMixin (Core) { Core.header = function (menu) { menu.push({ 'text': '文章', 'icon': 'icon icon-article', 'uri': '/content' }) } }
##路由注入
如上(默認(rèn)導(dǎo)出模塊)述說(shuō),routerMixin 為模塊 Core 注入了 Core.router 的實(shí)現(xiàn),具體代碼如下:
```ecmascript 6
import ContentArticle from '../components/Article'
import ContentArticleCreate from '../components/ArticleCreate'
import ContentArticleDraft from '../components/ArticleDraft'
import ContentArticleDraftEdit from '../components/ArticleDraftEdit'
import ContentArticleEdit from '../components/ArticleEdit'
import ContentArticleRecycle from '../components/ArticleRecycle'
import ContentCategory from '../components/ArticleCategory'
import ContentComment from '../components/Comment'
import ContentComponent from '../components/Component'
import ContentDashboard from '../components/Dashboard'
import ContentExtension from '../components/Extension'
import ContentLayout from '../components/Layout'
import ContentPage from '../components/Page'
import ContentPageCategory from '../components/PageCategory'
import ContentPageCreate from '../components/PageCreate'
import ContentPageEdit from '../components/PageEdit'
import ContentTemplate from '../components/Template'
import ContentTag from '../components/ArticleTag'
export function routerMixin (Core) {
Core.router = function (router) {
router.modules.push({
path: '/content',
component: ContentLayout,
children: [
{
path: '/',
component: ContentDashboard,
beforeEnter: router.auth
},
{
path: 'article/all',
component: ContentArticle,
beforeEnter: router.auth
},
{
path: 'article/create',
component: ContentArticleCreate,
beforeEnter: router.auth
},
{
path: 'article/:id/draft',
component: ContentArticleDraftEdit,
beforeEnter: router.auth
},
{
path: 'article/:id/edit',
component: ContentArticleEdit,
beforeEnter: router.auth
},
{
path: 'article/category',
component: ContentCategory,
beforeEnter: router.auth
},
{
path: 'article/tag',
component: ContentTag,
beforeEnter: router.auth
},
{
path: 'article/recycle',
component: ContentArticleRecycle,
beforeEnter: router.auth
},
{
path: 'article/draft',
component: ContentArticleDraft,
beforeEnter: router.auth
},
{
path: 'page/all',
component: ContentPage,
beforeEnter: router.auth
},
{
path: 'page/create',
component: ContentPageCreate,
beforeEnter: router.auth
},
{
path: 'page/:id/edit',
component: ContentPageEdit,
beforeEnter: router.auth
},
{
path: 'page/category',
component: ContentPageCategory,
beforeEnter: router.auth
},
{
path: 'component',
component: ContentComponent,
beforeEnter: router.auth
},
{
path: 'template',
component: ContentTemplate,
beforeEnter: router.auth
},
{
path: 'extension',
component: ContentExtension,
beforeEnter: router.auth
},
{
path: 'comment',
component: ContentComment,
beforeEnter: router.auth
}
]
})
}
}
Core.router 的調(diào)用者,為該方法提供了一個(gè) router 對(duì)象,該 router 對(duì)象中包含如下特性:
側(cè)邊欄菜單注入,提供了擴(kuò)展管理子級(jí)菜單的注入,由 Core.sidebar 提供注入,代碼參考如下:
```ecmascript 6 export default { sidebar: function (sidebar) { sidebar.push({ text: '多說(shuō)評(píng)論', icon: 'fa fa-comment', uri: '/duoshuo' }) } }
##前端擴(kuò)展構(gòu)建和打包
在進(jìn)行代碼編寫(xiě)和相關(guān)配置之后,使用命令 npm run build 即可完成對(duì)擴(kuò)展模塊的打包。
##前端資源注入
通過(guò)前端工具構(gòu)建和打包后,可以得到前端靜態(tài)資源文件(js文件,css文件,圖片文件等),可以模塊中的類(lèi) ModuleServiceProvider 或擴(kuò)展中的類(lèi) Extension 中將靜態(tài)資源文件發(fā)布到 public 目錄下。
類(lèi) ModuleServiceProvider 的代碼參考如下:
```php
<?php
/**
* This file is part of Notadd.
*
* @author TwilRoad <269044570@qq.com>
* @copyright (c) 2016, notadd.com
* @datetime 2016-10-08 17:12
*/
namespace Notadd\Content;
use Illuminate\Support\ServiceProvider;
/**
* Class Module.
*/
class ModuleServiceProvider extends ServiceProvider
{
/**
* Boot service provider.
*/
public function boot()
{
$this->publishes([
realpath(__DIR__ . '/../resources/mixes/administration/dist/assets/content/administration') => public_path('assets/content/administration'),
realpath(__DIR__ . '/../resources/mixes/foreground/dist/assets/content/foreground') => public_path('assets/content/foreground'),
], 'public');
}
}
然而,這樣并沒(méi)有結(jié)束,仍然需要告訴 Administration 模塊你提供了哪些靜態(tài)資源文件,給后臺(tái)的前端頁(yè)面使用。
在模塊中的類(lèi) ModuleServiceProvider 或擴(kuò)展中的類(lèi) Extension 中提供了相應(yīng)注入點(diǎn),script 方法將告訴后臺(tái)的前端頁(yè)面引用前面打包生成的 UMD 模塊文件,stylesheet 方法將告訴后臺(tái)的前端頁(yè)面引用前面打包生成樣式文件。
具體代碼參考如下:
<?php
/**
* This file is part of Notadd.
*
* @author TwilRoad <269044570@qq.com>
* @copyright (c) 2016, notadd.com
* @datetime 2016-10-08 17:12
*/
namespace Notadd\Content;
use Illuminate\Support\ServiceProvider;
/**
* Class Module.
*/
class ModuleServiceProvider extends ServiceProvider
{
/**
* Boot service provider.
*/
public function boot()
{
}
/**
* Get script of extension.
*
* @return string
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
public static function script()
{
return asset('assets/content/administration/js/module.js');
}
/**
* Get stylesheet of extension.
*
* @return array
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
public static function stylesheet()
{
return [
asset('assets/content/administration/css/module.css'),
];
}
}
更多建議: