-
Notifications
You must be signed in to change notification settings - Fork 0
/
content.json
1 lines (1 loc) · 24.4 KB
/
content.json
1
{"meta":{"title":"果果家","subtitle":"记录中","description":null,"author":"Zhang Yuan","url":"http://blog.guoguojia.net"},"pages":[{"title":"tags","date":"2019-01-01T10:09:29.000Z","updated":"2019-01-01T10:10:25.483Z","comments":false,"path":"tags/index.html","permalink":"http://blog.guoguojia.net/tags/index.html","excerpt":"","text":""},{"title":"categories","date":"2019-01-01T10:10:35.000Z","updated":"2019-01-01T10:11:08.391Z","comments":false,"path":"categories/index.html","permalink":"http://blog.guoguojia.net/categories/index.html","excerpt":"","text":""}],"posts":[{"title":"Javascript中的异步操作一--回调","slug":"Javascript中的异步操作一-回调函数","date":"2019-01-08T04:20:40.000Z","updated":"2019-01-08T04:20:40.864Z","comments":true,"path":"2019/01/08/Javascript中的异步操作一-回调函数/","link":"","permalink":"http://blog.guoguojia.net/2019/01/08/Javascript中的异步操作一-回调函数/","excerpt":"","text":"本文翻译自(https://javascript.info/callbacks)[https://javascript.info/callbacks] 回调JavaScript中有许多操作都是异步的,例如下面的loadScript(src): 12345function loadScript(src) { let script = document.createElement('script'); script.src = src; document.head.append(script);} 这个函数是用来加载新的脚本的,运行时把<script src="…">添加到文档流中,当浏览器解析到这个地方时执行。可以这样使用:12// 加载并执行脚本loadScript('/my/script.js'); 这个方法被称为异步函数,因为这个加载脚本的操作不是立刻完成,而是稍后完成的。 这个方法首先初始化脚本,然后执行代码。在脚本加载的过程中,这个方法下的代码有可能已经执行完毕,如果这个过程非常耗时,那其余的代码也有可能同时执行。 123loadScript('/my/script.js');// loadScript方法下面的代码不会等待其执行完毕才开始执行// ... 假设我们想调用通过loadScript方法加载到的代码中的newFunction方法,你会发现下面的代码不会执行: 123loadScript('/my/script.js'); // 脚本中有\"function newFunction() {…}\"newFunction(); // 没有这个方法!!! 浏览器可能来不及加载完整的脚本,因此立即调用newFunction方法会失败,在当前的情况下,loadScript方法不能追踪代码加载的完成与否,脚本加载并最终执行,这就是这段代码做的事情,但是我们需要知道具体什么时间加载,什么时候开始执行才能正确使用脚本中的新方法和变量。 我们来为loadScipt方法加上第二个参数callback,来作为脚本加载完后执行的回调。 12345678function loadScript(src, callback) { let script = document.createElement('script'); script.src = src; script.onload = () => callback(script); document.head.append(script);} 现在可以将脚本中定义的方法写在回调中来调用它: 12345loadScript('/my/script.js', function() { // the callback runs after the script is loaded newFunction(); // 现在可以执行了 ...}); 这就对了,第二个参数是脚本加载完成后执行的函数(通常是匿名函数). 以下是一个现实中的可执行的示例: 1234567891011function loadScript(src, callback) { let script = document.createElement('script'); script.src = src; script.onload = () => callback(script); document.head.append(script);}loadScript('https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.2.0/lodash.js', script => { alert(`Cool, the ${script.src} is loaded`); alert( _ ); // function declared in the loaded script}); 这就是所谓的”基于回调的”异步编程方式,一个异步执行的方法应该包括一个回调方法供其执行完毕后调用。 这里是以loadScript为例来说明的,是一种通行的做法。 回调中的回调那么如何顺序加载两段脚本呢? 自然而然会想到把第二段loadScript脚本放在第一段loadScript脚本的回调中: 123456789loadScript('/my/script.js', function(script) { alert(`Cool, the ${script.src} is loaded, let's load one more`); loadScript('/my/script2.js', function(script) { alert(`Cool, the second script is loaded`); });}); 当外层的loadScript加载完毕后,再加载里面的回调。 那如果再加更多层的脚本呢? 1234567891011loadScript('/my/script.js', function(script) { loadScript('/my/script2.js', function(script) { loadScript('/my/script3.js', function(script) { // ...continue after all scripts are loaded }); })}); 每一个新的方法都嵌套在一个回调中,这样多几层还行,再多层就不好使了,随后我们会看到其他的变通方法。 错误处理在上面的例子中我们没有考虑错误的情况。假如脚本加载失败呢,我们的代码理应对错误做出反应。 这是可以追踪加载错误的loadScript改进版本代码: 123456789function loadScript(src, callback) { let script = document.createElement('script'); script.src = src; script.onload = () => callback(null, script); script.onerror = () => callback(new Error(`Script load error for ${src}`)); document.head.append(script);} 对于成功的加载调用callback(null, script), 对于失败的加载调用callback(error). 使用方法: 1234567loadScript('/my/script.js', function(error, script) { if (error) { // 处理错误 } else { // 脚本成功加载 }}); 以上的用法非常常见,称作”错误优先回调”. 约定如下: 回调的第一个参数保留用作错误处理,当错误发生时,调用callback(err) 第二个及后面的参数供成功的情况下使用,然后调用callback(null, result1, result2…) 这样callback方法就可以同时用来报错和返回结果了。 嵌套的噩梦第一眼看上去,这种异步编程模式切实可行,对于一到两层的嵌套看上去不错,但是对于更多层的嵌套就不是这么回事了: 1234567891011121314151617181920212223loadScript('1.js', function(error, script) { if (error) { handleError(error); } else { // ... loadScript('2.js', function(error, script) { if (error) { handleError(error); } else { // ... loadScript('3.js', function(error, script) { if (error) { handleError(error); } else { // ...在所有脚本都加载好后执行 (*) } }); } }) }}); 上面的代码中: 加载1.js,如果没有报错,然后 加载2.js, 如果没有报错,然后 加载3.js,如果没有报错,然后继续执行 当嵌套更多的层级时,代码就会越来越难以维护,特别是...处换成包含各种循环、条件判断的真实的代码。 这就被称作回调地狱或嵌套的噩梦。 这种代码不是非常好,可以尝试把每一步写成单独的方法来缓解这个问题,像这样: 123456789101112131415161718192021222324252627loadScript('1.js', step1);function step1(error, script) { if (error) { handleError(error); } else { // ... loadScript('2.js', step2); }}function step2(error, script) { if (error) { handleError(error); } else { // ... loadScript('3.js', step3); }}function step3(error, script) { if (error) { handleError(error); } else { // ...continue after all scripts are loaded (*) }}; 这段代码把代码的嵌套写成了单独的方法,做的事情还是一样的。 这种方法起作用,但代码看起来像是一段撕碎的电子表格,需要眼球跳来跳去,很难阅读。这很不方便,特别是读者不熟悉代码的情况下都不知道眼球应该放哪里。 还有一个问题是命名为step*的方法是用来避免嵌套噩梦的,都只能用一次,没有人会在方法链外再次使用,因此可能会造成命名空间的混杂。 我们需要更好的方法来处理这个问题。 幸运的是,的确有办法可以避免这样的噩梦,那就是下一章介绍的promises(保证)。","categories":[],"tags":[{"name":"Javascript","slug":"Javascript","permalink":"http://blog.guoguojia.net/tags/Javascript/"},{"name":"语法","slug":"语法","permalink":"http://blog.guoguojia.net/tags/语法/"},{"name":"异步操作","slug":"异步操作","permalink":"http://blog.guoguojia.net/tags/异步操作/"}]},{"title":"Laravel最佳实践","slug":"laravel-best-practices","date":"2019-01-03T03:42:41.000Z","updated":"2019-01-03T07:03:48.270Z","comments":true,"path":"2019/01/03/laravel-best-practices/","link":"","permalink":"http://blog.guoguojia.net/2019/01/03/laravel-best-practices/","excerpt":"","text":"翻译自:https://github.com/alexeymezenin/laravel-best-practices contents单一责任原则胖模型,瘦控制器校验业务逻辑应该写在服务类中不要重复自己的代码(DRY)尽量使用Eloquent与collections做数据库查询批量赋值不要在Blade模板中做查询,使用实时加载(N+1问题)给代码添加注释,尽量使用描述性的方法和变量名来取代代码注释Blade模板中不要加JS和CSS,不要在PHP类中加HTML使用配置、语言文件和定义常量来取代代码中的文字使用被社区广为接受的标准Laravel工具遵循Laravel命名规范在可能的地方使用更短可读性更强的句法用控制反转容器或门面来取代new一个新的对象不要直接从.env文件中读取数据以标准格式存储数据,使用存取器和变异器来修改日期格式其他好的实践 single-responsibility-principle单一责任原则一个类,一个方法应该只做一件事情 不好的做法: 12345678public function getFullNameAttribute(){ if (auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified()) { return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name; } else { return $this->first_name[0] . '. ' . $this->last_name; }} 好的做法: 12345678910111213141516171819public function getFullNameAttribute(){ return $this->isVerifiedClient() ? $this->getFullNameLong() : $this->getFullNameShort();}public function isVerifiedClient(){ return auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified();}public function getFullNameLong(){ return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;}public function getFullNameShort(){ return $this->first_name[0] . '. ' . $this->last_name;} 🔝 回到顶部 fat-models-skinny-controllers胖模型,瘦控制器不要使用查询构造器或SQL语句来查询数据库,把所有有关数据库操作的代码写在Eloquent模型或Repository类中 不好的做法: 12345678910public function index(){ $clients = Client::verified() ->with(['orders' => function ($q) { $q->where('created_at', '>', Carbon::today()->subWeek()); }]) ->get(); return view('index', ['clients' => $clients]);} 好的做法: 12345678910111213141516public function index(){ return view('index', ['clients' => $this->client->getWithNewOrders()]);}class Client extends Model{ public function getWithNewOrders() { return $this->verified() ->with(['orders' => function ($q) { $q->where('created_at', '>', Carbon::today()->subWeek()); }]) ->get(); }} 🔝 回到顶部 validation校验把表单校验逻辑从控制器挪到请求类中 不好的做法 12345678910public function store(Request $request){ $request->validate([ 'title' => 'required|unique:posts|max:255', 'body' => 'required', 'publish_at' => 'nullable|date', ]); ....} 好的做法: 12345678910111213141516public function store(PostRequest $request){ ....}class PostRequest extends Request{ public function rules() { return [ 'title' => 'required|unique:posts|max:255', 'body' => 'required', 'publish_at' => 'nullable|date', ]; }} 🔝 回到顶部 business-logic-should-be-in-service-class业务逻辑应该写在服务类中一个控制器必须只做一件事情,因此把业务逻辑从控制器挪到服务类中 不好的做法: 12345678public function store(Request $request){ if ($request->hasFile('image')) { $request->file('image')->move(public_path('images') . 'temp'); } ....} 好的做法: 12345678910111213141516public function store(Request $request){ $this->articleService->handleUploadedImage($request->file('image')); ....}class ArticleService{ public function handleUploadedImage($image) { if (!is_null($image)) { $image->move(public_path('images') . 'temp'); } }} 🔝 回到顶部 dont-repeat-yourself-dry不要重复自己的代码(DRY)尽可能的复用代码,包括blade模板中的代码和使用Eloquent scope 不好的做法: 1234567891011public function getActive(){ return $this->where('verified', 1)->whereNotNull('deleted_at')->get();}public function getArticles(){ return $this->whereHas('user', function ($q) { $q->where('verified', 1)->whereNotNull('deleted_at'); })->get();} 好的做法: 12345678910111213141516public function scopeActive($q){ return $q->where('verified', 1)->whereNotNull('deleted_at');}public function getActive(){ return $this->active()->get();}public function getArticles(){ return $this->whereHas('user', function ($q) { $q->active(); })->get();} 🔝 回到顶部 Prefer to use Eloquent over using Query Builder and raw SQL queries. Prefer collections over arrays尽量使用Eloquent与collections做数据库查询Eloquent让你可以写出可读性强、可维护性强的代码,它还支持软删除、事件操作等等优势 不好的做法: 123456789101112SELECT *FROM `articles`WHERE EXISTS (SELECT * FROM `users` WHERE `articles`.`user_id` = `users`.`id` AND EXISTS (SELECT * FROM `profiles` WHERE `profiles`.`user_id` = `users`.`id`) AND `users`.`deleted_at` IS NULL)AND `verified` = '1'AND `active` = '1'ORDER BY `created_at` DESC 好的做法: 1Article::has('user.profile')->verified()->latest()->get(); 🔝 回到顶部 Mass assignment批量赋值不好的做法: 1234567$article = new Article;$article->title = $request->title;$article->content = $request->content;$article->verified = $request->verified;// Add category to article$article->category_id = $category->id;$article->save(); 好的做法: 1$category->article()->create($request->all()); 🔝 回到顶部 Do not execute queries in Blade templates and use eager loading (N + 1 problem)不要在Blade模板中做查询,使用实时加载(N+1问题)不好的做法(对于100个用户,做了101次查询): 123@foreach (User::all() as $user) {{ $user->profile->name }}@endforeach 好的做法(对于100个用户,只做了两次查询): 1234567$users = User::with('profile')->get();...@foreach ($users as $user) {{ $user->profile->name }}@endforeach 🔝 回到顶部 Comment your code, but prefer descriptive method and variable names over comments给代码添加注释,尽量使用描述性的方法和变量名来取代代码注释不好的做法: 1if (count((array) $builder->getQuery()->joins) > 0) 好一些的做法: 12// Determine if there are any joins.if (count((array) $builder->getQuery()->joins) > 0) 很好的做法: 1if ($this->hasJoins()) 🔝 回到顶部 Do not put JS and CSS in Blade templates and do not put any HTML in PHP classesBlade模板中不要加JS和CSS,不要在PHP类中加HTML不好的做法: 1let article = `{{ json_encode($article) }}`; 好一些的做法: 12345<input id=\"article\" type=\"hidden\" value=\"@json($article)\">Or<button class=\"js-fav-article\" data-article=\"@json($article)\">{{ $article->name }}<button> 在Javascript文件中: 1let article = $('#article').val(); 最好用专门的代码在PHP端包装数据,在JS端解析数据 🔝 回到顶部 Use config and language files, constants instead of text in the code使用配置、语言文件和定义常量来取代代码中的文字不好的做法: 123456public function isNormal(){ return $article->type === 'normal';}return back()->with('message', 'Your article has been added!'); 好的做法: 123456public function isNormal(){ return $article->type === Article::TYPE_NORMAL;}return back()->with('message', __('app.article_added')); 🔝 回到顶部 Use standard Laravel tools accepted by community使用被社区广为接受的标准Laravel工具不要任意使用第三方的包和工具,要选用被社区广为接受的标准Laravel工具,不要坑你的客户 任务 标准工具 第三方工具 Authorization Policies Entrust, Sentinel and other packages Compiling assets Laravel Mix Grunt, Gulp, 3rd party packages Development Environment Homestead Docker Deployment Laravel Forge Deployer and other solutions Unit testing PHPUnit, Mockery Phpspec Browser testing Laravel Dusk Codeception DB Eloquent SQL, Doctrine Templates Blade Twig Working with data Laravel collections Arrays Form validation Request classes 3rd party packages, validation in controller Authentication Built-in 3rd party packages, your own solution API authentication Laravel Passport 3rd party JWT and OAuth packages Creating API Built-in Dingo API and similar packages Working with DB structure Migrations Working with DB structure directly Localization Built-in 3rd party packages Realtime user interfaces Laravel Echo, Pusher 3rd party packages and working with WebSockets directly Generating testing data Seeder classes, Model Factories, Faker Creating testing data manually Task scheduling Laravel Task Scheduler Scripts and 3rd party packages DB MySQL, PostgreSQL, SQLite, SQL Server MongoDB 🔝 回到顶部 Follow Laravel naming conventions遵循Laravel命名规范遵循PSR规范 PSR标准 同时遵循Laravel社区接受的命名传统 称呼什么 怎么样的 好的 不好的 Controller singular ArticleController ArticlesController Route plural articles/1 article/1 Named route snake_case with dot notation users.show_active users.show-active, show-active-users Model singular User Users hasOne or belongsTo relationship singular articleComment articleComments, article_comment All other relationships plural articleComments articleComment, article_comments Table plural article_comments article_comment, articleComments Pivot table singular model names in alphabetical order article_user user_article, articles_users Table column snake_case without model name meta_title MetaTitle; article_meta_title Model property snake_case $model->created_at $model->createdAt Foreign key singular model name with _id suffix article_id ArticleId, id_article, articles_id Primary key - id custom_id Migration - 2017_01_01_000000_create_articles_table 2017_01_01_000000_articles Method camelCase getAll get_all Method in resource controller table store saveArticle Method in test class camelCase testGuestCannotSeeArticle test_guest_cannot_see_article Variable camelCase $articlesWithAuthor $articles_with_author Collection descriptive, plural $activeUsers = User::active()->get() $active, $data Object descriptive, singular $activeUser = User::active()->first() $users, $obj Config and language files index snake_case articles_enabled ArticlesEnabled; articles-enabled View snake_case show_filtered.blade.php showFiltered.blade.php, show-filtered.blade.php Config snake_case google_calendar.php googleCalendar.php, google-calendar.php Contract (interface) adjective or noun Authenticatable AuthenticationInterface, IAuthentication Trait adjective Notifiable NotificationTrait 🔝 回到顶部 Use shorter and more readable syntax where possible在可能的地方使用更短可读性更强的句法不好的做法: 12$request->session()->get('cart');$request->input('name'); 好的做法: 12session('cart');$request->name; 更多的例子: 一般的句法 短小精悍的句法 Session::get('cart') session('cart') $request->session()->get('cart') session('cart') Session::put('cart', $data) session(['cart' => $data]) $request->input('name'), Request::get('name') $request->name, request('name') return Redirect::back() return back() is_null($object->relation) ? null : $object->relation->id optional($object->relation)->id return view('index')->with('title', $title)->with('client', $client) return view('index', compact('title', 'client')) $request->has('value') ? $request->value : 'default'; $request->get('value', 'default') Carbon::now(), Carbon::today() now(), today() App::make('Class') app('Class') ->where('column', '=', 1) ->where('column', 1) ->orderBy('created_at', 'desc') ->latest() ->orderBy('age', 'desc') ->latest('age') ->orderBy('created_at', 'asc') ->oldest() ->select('id', 'name')->get() ->get(['id', 'name']) ->first()->name ->value('name') 🔝 回到顶部 Use IoC container or facades instead of new Class用控制反转容器或门面来取代new一个新的对象new一个新的对象的这种做法造成了类与复杂测试之间的强耦合,尽量使用控制反转或门面 不好的做法: 12$user = new User;$user->create($request->all()); 好的做法: 12345678public function __construct(User $user){ $this->user = $user;}....$this->user->create($request->all()); 🔝 回到顶部 Do not get data from the .env file directly不要直接从.env文件中读取数据把.env文件中的数据先传递到config文件中,再在应用中通过config()帮助方法使用 不好的做法: 1$apiKey = env('API_KEY'); 好的做法: 12345// config/api.php'key' => env('API_KEY'),// Use the data$apiKey = config('api.key'); 🔝 回到顶部 Store dates in the standard format. Use accessors and mutators to modify date format以标准格式存储数据,使用存取器和变异器来修改日期格式不好的做法: 12{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->toDateString() }}{{ Carbon::createFromFormat('Y-d-m H-i', $object->ordered_at)->format('m-d') }} 好的做法: 12345678910// Modelprotected $dates = ['ordered_at', 'created_at', 'updated_at']public function getSomeDateAttribute($date){ return $date->format('m-d');}// View{{ $object->ordered_at->toDateString() }}{{ $object->ordered_at->some_date }} 🔝 回到顶部 Other good practices其他好的实践不要在路径文件里写任何的逻辑 Blade模板中尽量不要使用原生的PHP语法 🔝 回到顶部","categories":[],"tags":[{"name":"Laravel","slug":"Laravel","permalink":"http://blog.guoguojia.net/tags/Laravel/"},{"name":"实践","slug":"实践","permalink":"http://blog.guoguojia.net/tags/实践/"}]},{"title":"这是第一篇测试文章","slug":"first-post","date":"2019-01-01T08:56:33.000Z","updated":"2019-01-01T08:56:33.989Z","comments":true,"path":"2019/01/01/first-post/","link":"","permalink":"http://blog.guoguojia.net/2019/01/01/first-post/","excerpt":"","text":"测试这里是第一篇文章","categories":[{"name":"Hexo","slug":"Hexo","permalink":"http://blog.guoguojia.net/categories/Hexo/"}],"tags":[{"name":"测试","slug":"测试","permalink":"http://blog.guoguojia.net/tags/测试/"},{"name":"MD","slug":"MD","permalink":"http://blog.guoguojia.net/tags/MD/"}]},{"title":"Hello World","slug":"hello-world","date":"2019-01-01T08:41:05.054Z","updated":"2019-01-01T08:41:05.054Z","comments":true,"path":"2019/01/01/hello-world/","link":"","permalink":"http://blog.guoguojia.net/2019/01/01/hello-world/","excerpt":"","text":"Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub. Quick StartCreate a new post1$ hexo new \"My New Post\" More info: Writing Run server1$ hexo server More info: Server Generate static files1$ hexo generate More info: Generating Deploy to remote sites1$ hexo deploy More info: Deployment","categories":[],"tags":[]}]}