laravel composer 扩展包开发

原文 https://blog.csdn.net/m0sh1/article/details/79257935

我按照这个步骤已经走通

可以正常使用

我发布的包地址在

https://packagist.org/packages/leokim/learning-pack#dev-master

git地址在:https://github.com/jl621/learning-pack

image.png

文章适用于laravel 包开发,当然如果你理解着完成一遍,就可以发现他也适用于 composer 扩展包开发,不是必须在laravel 下。
首先在 laravel 根目录创建文件夹 packages 这里放置我们准备创建的扩展包,这个目录只是暂时存放我们的扩展包代码,等我们开发完成配置好了,就不需要他了。
当然如果你不需要发布你的包,以后也可以就使用这个目录。packages 目录和 laravel 的 app 目录同级
然后进入packages 创建目录  leokim当然这个名字可以随意起(最好是作者的名之类的),
接着进入 leokim目录创建目录 packagetest 这个目录的名称最好是你的扩展包名称,有点意义。 我就是为了测试,所以就叫做 packagetest 好了
然后创建目录 src 这里就是我们放置代码的地方啦。
接着命令行下进入 packages/leokim/learning执行 composer init  他会一步步询问你要填写的信息:
这里写图片描述
执行完成你会在 packagetest 目录下看到 composer.json 内容和上图一致。 当然其实你也可以直接复制一个 composer.json 不需要 composer init
我的 composer.json 内容如下:

{
    "name": "leokim/learning-pack",
    "authors": [
        {
            "name": "Leo Kim",
            "email": "admin@leokim.cn"
        }
    ],
    "require": {}
}

你也可以根据 composer.json 的规则添加相应的其它配置
目前目录结构是这样的:
这里写图片描述
虽然你知道代码都在 src目录下,但是 laravel 不知道,所以你要告诉他,即配置 laravel 根目录的 composer.json
修改 autoload 改为类似如下:

"autoload": {
    "classmap": [
        "database/seeds",
        "database/factories"
    ],
    "psr-4": {
        "App\\": "app/",
        "Leokim\\learningPack":"packages/leokim/learning-pack/src"
    }
},

然后创建服务:使用 artisan 命令

php artisan make:provider PackagetestServiceProvider1

执行完成,laravel 在 app/Providers下会生成 LearningPackServiceProvider.php 然后你把他剪切到 你的 src目录:packages/leokim/learning-pack/src
同时修改代码的命名空间为你刚刚定义的:namespace LeoKim\LearningPack;  顺便把注册服务等都写完吧,完成代码如下:

<?php

namespace LeoKim\LearningPack;

use Illuminate\Support\ServiceProvider;

class LearningPackServiceProvider extends ServiceProvider
{
    /**
     * 服务提供者加是否延迟加载.
     *
     * @var bool
     */
    protected $defer = true; // 延迟加载服务
    /**
     * Bootstrap the application services.
     *
     * @return void
     */
    public function boot()
    {
        $this->loadViewsFrom(__DIR__ . '/views', 'LearningPack'); // 视图目录指定
        $this->publishes([
            __DIR__.'/views' => base_path('resources/views/vendor/LearningPack'),  // 发布视图目录到resources 下
            __DIR__.'/config/LearningPack.php' => config_path('LearningPack.php'), // 发布配置文件到 laravel 的config 下
        ]);
    }
    /**
     * Register the application services.
     *
     * @return void
     */
    public function register()
    {
        // 单例绑定服务
        $this->app->singleton('LearningPack', function ($app) {
            return new LearningPack($app['session'], $app['config']);
        });
    }
    /**
     * Get the services provided by the provider.
     *
     * @return array
     */
    public function provides()
    {
        // 因为延迟加载 所以要定义 provides 函数 具体参考laravel 文档
        return ['LearningPack'];
    }
}

自问自答:
1.为什么创建的服务要放到src 下? – 你要开发扩展包,放到laravel下面就不算扩展包了,你的包以后要给别人用,别人会统一安装到vendor下的,总不能单独把 service 文件也打包上传吧。
同理服务定义了 publish , 配置和视图不同系统需求肯定不一样,为了让人家修改,所以我们提供发布到laravel 原始视图和配置路径的方法,总不能让人家下载了你的到 到 vendor下修改吧。
2.那么 composer.json 里的命名空间为什么修改的是laravel 根目录的? – 啪!多嘴!哦,不对,啪啪啪啪!!! 问的好!,这个我们还没讲完嘛,后面会给他提出来的,我们需要先跑通我们的代码,再完善成可发布的
接下来注册我们的服务到 config/app.php (你使用别人家的包都需要这步的)
添加一行 LeoKim\LearningPack\LearningPackServiceProvider::class
下一步添加配置文件:
在 src 目录下添加 config 目录然后添加文件 LearningPack.php

<?php
return [
    'options' => []
];

下一步创建我们的服务真正逻辑实现的代码: 在src目录下创建文件 LearningPack.php

<?php
namespace LeoKim\LearningPack;
use Illuminate\Session\SessionManager;
use Illuminate\Config\Repository;
class LearningPack
{
    /**
     * @var SessionManager
     */
    protected $session;
    /**
     * @var Repository
     */
    protected $config;
    /**
     * Packagetest constructor.
     * @param SessionManager $session
     * @param Repository $config
     */
    public function __construct(SessionManager $session, Repository $config)
    {
        $this->session = $session;
        $this->config = $config;
    }
    /**
     * @param string $msg
     * @return string
     */
    public static function test_rtn($msg = ''){
//        $config_arr = $this->config->get('LearningPack.options');
        return $msg.' <strong>from your custom develop package!</strong>';
    }
}

下一步创建视图文件:在src目录下添加views目录然后添加 LearningPack.blade.php

<h1>Learning Pack Message</h1>
{!! $msg !!}

下一步创建门面(Facades): 在src目录下添加 Facades目录然后添加 LearningPack.php

<?php
namespace LeoKim\LearningPack\Facades;
use Illuminate\Support\Facades\Facade;
class LearningPack extends Facade
{
    protected static function getFacadeAccessor()
    {
        return 'learningpack';
    }
}

然后命令行执行 :

composer dump-autoload1

这样就能够使用命名空间 LeoKim\LearningPack了,上面在 config/app.php 下添加的服务那行就真正生效了。(如果不执行 dump-autoload 运行程序会报错,说找不到类)
既然我们定义了门面 那么我们就可以为这个服务添加别名了。在 config/app.php  的 aliases 数组添加一行:

LeoKim\LearningPack\LearningPackServiceProvider::class,

现在我们的目录结构类似:
image.png 
至此代码其实就已经跑通了,但是还没有完全完成。我们先测试下试试,随便找个 controller 

当然 route要定义好:

Route::get('test', 'TestController@index');

例如:TestController.php

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use LeoKim\LearningPack\LearningPack;


class TestController extends Controller
{


    public function index(Request $request){
        $a = LearningPack::test_rtn('LeoKim');

        return view('vendor.LearningPack.LearningPack', ['msg'=>$a]);
    }
}

然后根据路由访问就可以看到效果啦。为什么说没有完全完成呢?因为 视图文件和 config 配置文件还在我们的包里定义,以后发布出去,包会在 vendor目录下,这些文件不应该在vendor下修改
所以命令行执行:

php artisan vendor:publish --provider="LeoKim\LearningPack\LearningPackServiceProvider"

发布后你就会在 laravel 本身的 config目录 和 views/vendor/LearningPack下看到你的文件了,也就可以按照需求随意修改了。
最后我们说 修改的laravel 的composer.json ,我们要发布我们的包,让所有人都能使用 composer 安装,那么执行如下步骤
去掉 添加的 那行 “LeoKim\LearningPack\”: “packages/leokim/learning-pack/src/” 

然后 修改 packages/leokim/learning-pack/composer.json
添加 autoload:

    "autoload": {
        "psr-4": {
            "LeoKim\\LearningPack\\": "src/"
        }
    }
}

这样包就是一个完整独立的包了,然后把他提交到你的 GitHub 上。

 
地址在文章头

接着就是把包提交到 packagelist了, 网址: https://packagist.org/ 如果没有账户则注册一个
然后点击 submit ,填写项目URL,点击check
这里写图片描述
成功后点击  submit 就完成了。 至此你的包就可以像其它人的一样通过 composer require 安装了 这里写图片描述
如上图,两个箭头分别代表了包名称 和 版本
所以安装这个包的时候你的 composer.json 在require可以加这样一行:

"leokim/learning-pack": "dev-master"

安装之前我们先把我们之前开发的这个包都删除吧,就假设是一个别人的 laravel 框架要用我们的包: 删除 packages 文件夹
删除 config/learningpack.php
删除 resources/views/vendor/learningpack
conifg/app.php 里面删除添加的服务和别名
controller 里的改动就保留吧,因为安装完还是要这么写一遍 最后执行 composer dump-autoload
下面安装这个自定义包吧: composer update leokim/learning-pack
然后添加服务: 修改 config/app.php 添加 

LeoKim\LearningPack\LearningPackServiceProvider::class,

和别名的配置: 

'LearningPack' => LeoKim\LearningPack\Facades\LearningPack::class

执行 composer dump-autoload
发布资源文件:

 php artisan vendor:publish --provider="LeoKim\LearningPack\LearningPackServiceProvider"

image.png

测试通过 大功告成!
image.png

额外的:
1.在 packagelist 你的这个包页面可以看到提示了 Set Up GitHub Service Hook 你可以按照提示办法安装,安装完成后,一旦你的项目有push,这里就会跟着更新。
2.还是 packagelist 页面,可以看到目前你只有  dev-master 版本,假设你需要其它的版本 你可以去你的 github 项目添加 tag
git tag 1.0.0 && git push –tags
这样composer require 就可以指定别的版本了。
3.为了别人能够更加清晰的使用你的包,完善你的 Readme 吧
4.不是必须laravel 框架,单纯的 composer 扩展包开发也是按照这个步骤来,只不过需要你摘出 laravel 结合的部分。

php7.2 dockerfile

FROM php:7.2-fpm
MAINTAINER goozp "gzp@goozp.com"
# 设置时区
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# 更新安装依赖包和PHP核心拓展
RUN apt-get update && apt-get install -y \
    libfreetype6-dev \
    libjpeg62-turbo-dev \
    libpng-dev \
    && docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \
    && docker-php-ext-install -j$(nproc) gd \
    && docker-php-ext-install zip \
    && docker-php-ext-install pdo_mysql \
    && docker-php-ext-install opcache \
    && docker-php-ext-install mysqli \
    && rm -r /var/lib/apt/lists/*
# 将预先下载好的拓展包从宿主机拷贝进去
COPY ./redis-4.1.1.tgz /home/redis.tgz
# 安装 PECL 拓展,这里我们安装的是Redis
RUN pecl install /home/redis.tgz && echo "extension=redis.so" > /usr/local/etc/php/conf.d/redis.ini
# 安装 Composer
#ENV COMPOSER_HOME /root/composer
#RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
#ENV PATH $COMPOSER_HOME/vendor/bin:$PATH
#RUN rm -f /home/redis.tgz \
WORKDIR /data
# Write Permission
RUN usermod -u 1000 www-dat

vue bus

vue Bus总线

有时候两个组件也需要通信(非父子关系)。当然Vue2.0提供了Vuex,但在简单的场景下,可以使用一个空的Vue实例作为中央事件总线。

参考:http://blog.csdn.net/u013034014/article/details/54574989?locationNum=2&fps=1

例子:https://segmentfault.com/q/1010000007491994

<divid="app">
<c1></c1>
<c2></c2>
</div>
var Bus = new Vue(); //为了方便将Bus(空vue)定义在一个组件中,在实际的运用中一般会新建一Bus.js
Vue.component('c1', { //这里已全局组件为例,同样,单文件组件和局部组件也同样适用
    template: '<div>{{msg}}</div>',
    data: () = >({
        msg: 'Hello World!'
    }),
    created() {
        Bus.$on('setMsg', content = >{
            this.msg = content;
        });
    }
});
Vue.component('c2', {
    template: '<button @click="sendEvent">Say Hi</button>',
    methods: {
        sendEvent() {
            Bus.$emit('setMsg', 'Hi Vue!');
        }
    }
});
varapp = newVue({
    el: '#app'
})

在实际运用中,一般将Bus抽离出来:Bus.js

import Vue from 'vue'
const Bus=newVue()
exportdefaultBus

组件调用时先引入

组件1

import Bus from './Bus'
exportdefault {
    data() {
        return {.........
        }
    },
    methods: {....Bus.$emit('log', 120)
    },
}

组件2

import Bus from './Bus'
exportdefault {
    data() {
        return {.........
        }
    },
    mounted() {
        Bus.$on('log', content = >{
            console.log(content)
        });
    }
}

但这种引入方式,经过webpack打包后可能会出现Bus局部作用域的情况,即引用的是两个不同的Bus,导致不能正常通信 

实际运用二(推荐):

当然也可以直接将Bus注入到Vue根对象中,

import Vue from 'vue'const Bus = newVue() varapp = newVue({
    el: '#app',
      data: {    Bus
    }  
})

在子组件中通过this.$root.Bus.$on(),this.$root.Bus.$emit()来调用

Vue2.0的三种常用传值方式、父传子、子传父、非父子组件传值

这里写图片描述

  1.  父组件向子组件传值

    父组件

    <template>
      <div>
        父组件:    <input type="text" v-model="name">
        <br>
        <br>
        <!-- 引入子组件 -->
        <child :inputName="name"></child>
      </div></template><script>
      import child from './child'
      export default {
        components: {
          child
        },
        data () {      return {
            name: ''
          }
        }
      }</script>

    子组件

    <template>
      <div>
        子组件:    <span>{{inputName}}</span>
      </div></template><script>
      export default {    // 接受父组件的值
        props: {
          inputName: String,
          required: true
        }
      }</script>

  2.  子组件向父组件传值

    子组件

    <template>
      <div>
        子组件:    <span>{{childValue}}</span>
        <!-- 定义一个子组件传值的方法 -->
        <input type="button" value="点击触发" @click="childClick">
      </div></template><script>
      export default {
        data () {      return {
            childValue: '我是子组件的数据'
          }
        },
        methods: {
          childClick () {        // childByValue是在父组件on监听的方法
            // 第二个参数this.childValue是需要传的值
            this.$emit('childByValue', this.childValue)
          }
        }
      }</script>

    父组件

    <template>
      <div>
        父组件:    <span>{{name}}</span>
        <br>
        <br>
        <!-- 引入子组件 定义一个on的方法监听子组件的状态-->
        <child v-on:childByValue="childByValue"></child>
      </div></template><script>
      import child from './child'
      export default {
        components: {
          child
        },
        data () {      return {
            name: ''
          }
        },
        methods: {
          childByValue: function (childValue) {
            // childValue就是子组件传过来的值
            this.name = childValue
          }
        }
      }</script>

     

  3.  非父子组件进行传值

    公共bus.js

    //bus.jsimport 
    Vue from 'vue'
    export default new Vue()

    组件A

    <template>
      <div>
        A组件:    <span>{{elementValue}}</span>
        <input type="button" value="点击触发" @click="elementByValue">
      </div></template><script>
      // 引入公共的bug,来做为中间传达的工具
      import Bus from './bus.js'
      export default {
        data () {      return {
            elementValue: 4
          }
        },
        methods: {
          elementByValue: function () {
            Bus.$emit('val', this.elementValue)
          }
        }
      }</script>

    组件B

    <template>
      <div>
        B组件:    <input type="button" value="点击触发" @click="getData">
        <span>{{name}}</span>
      </div></template><script>
      import Bus from './bus.js'
      export default {
        data () {      return {
            name: 0
          }
        },
        mounted: function () {
          var vm = this
          // 用$on事件来接收参数
          Bus.$on('val', (data) => {
            console.log(data)
            vm.name = data
          })
        },
        methods: {
          getData: function () {
            this.name++
          }
        }
      }</script>

     

Vue学习笔记

vue-cli3创建项目

一. 项目结构目录调整

初始文件添加

.editorconfig

image.png

再使用EditorConfig for VS Code插件

image.png

添加目录

/api

/directive 自定义指令

/lib 

/router

/store

image.png

/moke (npm isntall mockjs -D)

vue.config.js

image.png

基本配置修改