Резюме
Vue.js как передовая инфраструктура MVVM для внешнего интерфейса широко используется в различных сферах бизнеса на вынос. В этой статье излагаются основные преимущества Vue.js как интерфейсной среды MVVM, и она начинается с трех основных аспектов Vue.js: Observer, Watcher, Compiler, а также подробно описывает принципы проектирования и реализации Vue.js.
задний план
Vue.js — это облегченная интерфейсная среда MVVM, ориентированная на разработку слоя веб-представления (View). С 2013 года Vue.js привлекает все больше и больше разработчиков своим расширяемым механизмом привязки данных, чрезвычайно низкой стоимостью запуска, кратким и понятным API, эффективным и полным дизайном компонентов и другими функциями. На гитхабе уже 30000+ звёзд, и он постоянно растёт, имеет широкий спектр применения в стране и за рубежом, постоянно совершенствуется комьюнити и вспомогательные инструменты, а его влияние расширяется, что практически равняется «мировому рейтингу». фреймворки классов», такие как React и AngularJS. Студенты FE на стороне B ранее представили Vue.js для развития бизнеса (версия 0.10.x) После более чем года практики у них накопилось определенное понимание. Исходя из этого, мы хотим углубиться в Vue.js, а не просто придерживаться поверхности. Так что «чтение исходного кода» стало внеклассным заданием. Я лично начал читать исходный код Vue в сентябре, и читал его один за другим месяца 2. Вот мои заметки по изучению исходного кода. В этой статье мы надеемся сосредоточиться на дизайне и реализации версии 1.0 Vue.js и объяснить часть моего опыта чтения исходного кода.
Зачем внешнему интерфейсу нужен фреймворк MVVM?
История фронтенд-слэш-н-бёрна здесь повторяться не будет.
Когда данные обновляются, разработчик должен активно использовать DOM API для работы с DOM, когда DOM изменяется, разработчик должен активно получать данные DOM, синхронизировать или отправлять данные; Когда одни и те же данные сопоставляются с разными представлениями, требуется новый набор операций DOM, а возможность повторного использования низкая; Большое количество операций DOM делает бизнес-логику громоздкой и длинной, а ремонтопригодность кода оставляет желать лучшего. Итак, суть вопроса в следующем:
Бизнес-логика должна быть сосредоточена на манипулировании данными (моделью), а обновление модели DOM не требует множества явных операций. Синхронизируйте данные и представления (привязка данных и прослушивание обновлений) без вмешательства человека. Одни и те же данные могут легко соответствовать нескольким представлениям. Кроме того, некоторые функции, которые должны быть сделаны:
Удобная реализация логики представления (декларативная или императивная); Легко создавайте, подключайте и повторно используйте компоненты; Управление состоянием и маршрутизацией. Фреймворк MVVM может очень хорошо решить вышеуказанные проблемы. Представление и модель соединены мостом через ViewModel, а взаимодействие между Моделью и ViewModel является двунаправленным.Изменения данных Представления будут синхронизированы с Моделью, а изменения данных в Модели также будут немедленно отражены на Представление, которое мы обычно называем «двусторонней привязкой». Это не требует вмешательства человека, поэтому разработчикам нужно сосредоточиться только на бизнес-логике, а другие операции DOM и поддержание состояния реализуются инфраструктурой MVVM.
Why Vue?
Преимущества Vue.js в основном отражаются в:
Начальная стоимость для разработчиков очень низкая, а опыт разработки хороший. Если вы использовали angular, вы знаете, что в нем так много API, и разработчики должны быть знакомы с такими понятиями, как контроллер, директива, внедрение зависимостей, цикл дайджеста; Angular2 должен заранее понимать основы Typescript, RxJS и т. д.; Для новичка во фронтенде, чтобы справиться с семейством React, ES6 + Babel, функциональное программирование, JSX и инженерное проектирование также являются необходимыми пороговыми значениями. Vue.js не обременяет разработчиков и ограждает разработчиков от ряда сложных концепций.API очень оптимизирован с точки зрения количества и дизайна и поддерживает различные диалекты js, поэтому новички могут быстро освоить начали — конечно. Для студентов с определенным опытом вы также можете использовать популярные языки, фреймворки, библиотеки и инженерные инструменты для создания бесплатных и разумных словосочетаний.
Учитесь на сильных сторонах других и интегрируйте различные отличные функции — Vue.js имеет двустороннюю привязку данных, такую как angular, а версия 2.0 также предоставляет функции JSX, Virtual-DOM и изоморфизма на стороне сервера, такие как React; в то же время, vuex, vue-router, vue-cli и другие вспомогательные инструменты также образуют полноценную экосистему фреймворка.
Превосходное представление. Производительность Vue.js в версии 1.x была значительно лучше, чем у angular 1.x на основе грязной проверки (условной полной грязной проверки) за тот же период; в целом производительность версии Vue.js 1.x похож на React., и Vue.js не нужно вручную объявлять shouldComponentUpdate, как это делает React, чтобы оптимизировать производительность повторного рендеринга при изменении состояния. Версия Vue2.0 использует схему Virtual DOM + Dependency Tracking, а производительность дополнительно оптимизирована. Конечно, сравнение производительности вне зависимости от сцены — это хулиганство. В этом тесте сравнивается производительность основных интерфейсных фреймворков, и видно, что производительность Vue.js является одной из лучших в отрасли в большинстве сценариев.
Идеи дизайна привязки Vue.js
Согласно описанию вышеприведенного пространства, наиболее важной работой фреймворка MVVM является установление связи между представлением и моделью. То есть «привязка». Прикрепленное изображение из официальной документации иллюстрирует это:
Из приведенного выше рисунка мы можем получить только некоторое базовое (cu) базовое (qian) понимание:
Модель — это объект POJO, то есть простой объект javascript. Представление устанавливает связь привязки с моделью через прослушиватель DOM. Модель устанавливает отношения привязки с Представлением через Директивы (директивы), такие как {{a}} , v-text="a". На самом деле предстоит еще много работы:
Сделайте данные в модели реактивной, то есть при изменении состояния (изменения данных), система может ответить. Директивы (директивы) смешиваются в HTML-фрагменте (фрагмент или вы можете понять его как шаблон в экземпляре Vue). Директивы и выражения должны быть приняты правильно. Разные директивы должны соответствовать различным методам обновления DOM, которые являются проще всего понимать. Примеры V-iF и V-шоу; Обновление модели вызывает обновление представления инструкции, требует определенного механизма для того, чтобы; В разделе DOM прослушивателя различия между разными браузерами должны быть сглажены. С точки зрения реализации «Привязка» Vue.js предусматривает методы связывания и обновления всех директив, а именно:
Как связать данные после разбора инструкции Как обновить DOM при обновлении данных Решение Vue.js предлагает несколько основных концепций:
Наблюдатель: наблюдатель данных, defineReactive для всех данных модели, даже если все данные модели изменяются, он может уведомить подписчиков данных. Наблюдатель: Подписчик, подписывается и получает уведомления обо всех изменениях Модели, выполняет соответствующую функцию привязки инструкций (выражений). Dep : подписчик сообщений, используемый для сбора Watcher , уведомления подписчиков об обновлении при изменении данных. Компилятор: анализатор шаблона, который анализирует директивы, выражения и свойства (реквизиты) в шаблоне и привязывает соответствующую функцию обновления к представлению. Можно видеть, что основной идеей здесь является «режим наблюдателя», с которым знакомы все (особенно студенты FE). Общая идея конструкции такова:
Возвращаясь к только что упомянутому связыванию и обновлению, давайте посмотрим, как работают приведенные выше концепции:
В представлении инициализации, то есть на этапе связывания, Observer получает данные в new Vue() и назначает геттеры и сеттеры через Object.defineProperty; для данных в виде массивов массив также может быть уведомлен, когда он изменяется, с помощью захват некоторых методов. Кроме того, компилятор сканирует и анализирует инструкции узла DOM и подписывается на наблюдателя для обновления представления. Наблюдатели управляются в отделе подписчика сообщений. На этапе обновления, когда данные изменяются, срабатывает функция установки, и немедленно срабатывает соответствующее уведомление, начиная обход всех подписчиков, вызывая метод обновления своей инструкции для обновления представления. Хорошо, давайте продолжим рассмотрение трех «основных моментов», а именно принципов реализации Observer, Compiler и Watcher.
Принцип реализации наблюдателя данных Observer
Как упоминалось ранее, ядром Observer является определение реакции на данные в модели (данные). Реализация здесь следующая:
Когда Vue.js инициализирует данные, он сначала передает все свойства данных экземпляру Vue (удобно использовать это ключевое слово для доступа), а затем вызывает Observer для наблюдения за данными. Наблюдатель сначала пронаблюдает все свойства в данных в целом, определит свойство __ob__ и выполнит Object.defineProperty, то есть добавит наблюдателя к самим данным. Точно так же для каждого атрибута в данных также используйтеobДобавьте наблюдателей для каждого свойства. Точно так же, когда объект POJO определен со значением свойства, подобным следующему, он будет рекурсивно Object.defineProperty ;
{
data: {
a: {
b: "c"
}
},
d: [1, 2, 3]
}
Затем, когда определяемое значение свойства является массивом, когда сам массив изменяется через метод, также необходимо отслеживать изменение массива. Есть несколько способов управлять изменениями массива через javascript:
Изменения вносятся собственными методами массива, такими как push, pop и т. д.; Измените его через атрибут длины, например, arr.length = 0; Присвоение по индексу, например, arr[0] = 1 . Для собственных методов массива нам нужно инициировать события при использовании этих методов, чтобы сообщить системе, что массив изменился. Обычно мы думаем о способах «захватить» сам массив. Но очевидно, что мы не можем напрямую переопределить Array.prototype , такое глобальное переопределение, очевидно, повлияет на другие операции с массивами, которым не требуются отзывчивые данные. Идея Vue.js заключается в том, что когда значение атрибута данных определяется как массив, оно переопределяет значение массива значений атрибута.protoСвойства, то есть переменные прототипа, которые переопределяют только реактивные данные. Основная реализация выглядит следующим образом:
function Observer(value) {
this.value = value
this.dep = new Dep()
_.define(value, '__ob__', this)
// 如果判断当前值是Array, 进行劫持操作
if (_.isArray(value)) {
var augment = _.hasProto
? protoAugment
: copyAugment
// 在这里,arrayMethods是进行了劫持操作后的数组原型
// augment的作用即是覆写原型方法
augment(value, arrayMethods, arrayKeys)
this.observeArray(value)
} else {
// 递归地defineProperty
this.walk(value)
}
}
Для прямого изменения длины массива для изменения массива и присвоения индекса, очевидно, его нельзя напрямую захватить. В настоящее время метод реализации состоит в том, чтобы обернуть исходный массив в массив, подобный объекту, а затем определить в нем свойство, чтобы можно было выполнить назначение длины и угла. Но недостатком этого является то, что вам нужно явно вызывать этот объект каждый раз, когда вы используете массив, например:
var a = new ObservableArray([1, 2, 3, 4]);
Это, очевидно, увеличивает затраты разработчиков на начало работы, а изменить длину можно с помощью сплайсинга, поэтому Vue.js не реализует эту функцию, что является правильным выбором. Для назначения угловых меток по-прежнему существуют определенные сценарии использования, поэтому Vue.js расширяет методы $set и $remove для реализации. Эти два метода по-прежнему используют сращивание, которое можно взломать, а перехваченный метод может инициировать обновление представления.
example1.items[0] = { childMsg: 'Changed!'} // 不能触发视图更新
example1.items.$set(0, { childMsg: 'Changed!'}) // 可以触发视图更新
- Для мониторинга изменений объектов объекта существует предлагаемый метод Object.Observe (), но теперь он устарел стандартами браузера и больше не поддерживается браузерами. Существует также нестандартный метод часов. Object.watch (), который поддерживается Firefox, но не является универсальным.
- Мониторинг изменений объектов массива, а также на Array. Сохранение. Он не только контролирует метод массива, но и изменения в углас-назначении. Но это предложение также было отброшено.
- Теоретически использование объектов ES6 Proxy также может перехватывать get и set, но поддержка браузера не очень хороша, а осуществимость невысока.
- Поэтому Object.defineProperty по-прежнему остается лучшим выбором в нынешних условиях — конечно, в эпоху, когда популярны браузеры IE8 и ниже, MVVM-фреймворкам, основанным на этой фиче, сложно популяризироваться в больших масштабах.
Реализация компилятора парсера
Роль компилятора в основном состоит в том, чтобы анализировать входящий элемент el или template template, создавать соответствующий DOMFragment, извлекать директиву и выполнять метод, связанный с директивой, а также генерировать Watcher для каждой директивы. Основной точкой входа является метод _compile, смонтированный в Vue.prototype (фактическое содержимое немного отличается в instance/lifecycle.js, разных версиях Vue1.x). Поток всего метода _compile выглядит следующим образом:
Сначала выполните функцию transclude() , которая фактически обрабатывает тег шаблона или строку options.template, анализирует ее в DOMFragment и получает объект el. Затем выполните _initElement(el), чтобы смонтировать полученный объект el как экземпляр. Далее следует CompileRoot(el, options), который анализирует атрибуты (attrs) в DOM текущего корневого экземпляра; Затем выполните Compile(el, options), проанализируйте шаблон и верните ссылку Function(compositeLinkFn). Наконец, выполните этот составной LinkFn, создайте Конкретный процесс Compile(el, options) выглядит следующим образом:
Во-первых, основной функцией процесса компиляции является compileNode.Когда обнаруживается, что текущий узел имеет дочерние узлы, дерево DOM просматривается и анализируется путем рекурсивного вызова compileNode. Затем оцените узел и используйте comileElement или compileTextNode для синтаксического анализа. Мы видим, что окончательный результат компиляции возвращает составнойLinkFn, Функция этой функции состоит в том, чтобы создать экземпляр директивы, связать директиву с новым элементом и заменить элемент в дереве DOM. композитный LinkFn сначала выполнит Linkfn, созданный comileElement или compileTextNode, чтобы создать объект команды. При инициализации объекта инструкции вызывается не только привязка инструкции, но и определяется метод this._update, создается Наблюдатель и метод this._update (метод обновления, фактически соответствующий инструкции) используется как функция обратного вызова Watcher. Здесь Directive и Watcher связаны.Когда Watcher заметит, что значение выражения инструкции изменилось, он вызовет метод _update экземпляра Directive и, наконец, обновит узел DOM. Возьмите compileTextNode в качестве примера, напишите псевдокод для представления этого процесса:
// compile结束后返回此函数
function compositeLinkFn(arguments) {
linkAndCapture()
// 返回解绑指令函数,这里不深究。
return makeUnlinkFn(arguments)
}
function linkAndCapture(arguments) {
// 创建指令对象
linkFn()
// 遍历 directives 调用 dirs[i]._bind 方法对单个directive做一些绑定操作
// 这里会去实例化单个指令,执行指令的bind()函数,并创建Watcher
dirs[i]._bind()
}
// 解析TextNode节点,返回了linkFn
function compileTextNode(node) {
// 对节点数据进行解析,生成tokens
var tokens = textParser.parse(node.data)
createFragment()
// 创建token的描述,作为后续生成指令的依据
setTokenDescriptor()
/**
do other things
**/
return linkFn(tokens, ...);
}
// linkFn遍历token,遍历执行_bindDir, 传入token描述
function linkFn() {
tokens.forEach(function (token) {
if (token.html) replaceHtml();
vm._bindDir(token.discriptor)
})
}
// 根据token描述创建指令新实例
Vue.prototype._bindDir = function (descriptor) {
this._directives.push(new Directive(descriptor))
}
На этом работа компилятора закончена.
Внедрение мониторинга подписки Watcher
Обязанности наблюдателя
В реализации вышеупомянутого компилятора последним шагом является создание Watcher:
// создаем наблюдатель для каждой директивы каталоги[i]._bind() Directive.prototype._bind = функция () { ... // Создаем раздел Watcher var watcher = this._watcher = новый Watcher( это.вм, это.выражение, this._update, // обратный вызов { фильтры: this.filters, двухсторонний: this.twoWay, глубоко: это.глубоко, предварительный процесс: предварительный процесс, постпроцесс: постпроцесс, область: this._scope } ) } Полученными параметрами являются экземпляр vm, выражение, функция обратного вызова обратного вызова и соответствующая конфигурация Watcher, которая содержит информацию о контексте: this._scope. У каждой инструкции будет наблюдатель, который будет отслеживать значение выражения в режиме реального времени. Если оно изменится, он уведомит инструкцию о выполнении функции _update для обновления соответствующего DOM. Тогда можно считать, что основной работой наблюдателя является:
- Разбирать выражения, такие как v-show="a.b.c > 1" ; выражения необходимо преобразовать в функции для вычисления;
- Автоматически собирать зависимости.
Реализация наблюдателя
Выполнение этой части работы выглядит следующим образом:
Разрешение пути с использованием конечного автомата
Выражение синтаксического анализа здесь использует конечный автомат пути для эффективного синтаксического анализа пути. Подробный код см. в разделе parsers/path.js. Так называемый «путь» здесь относится к пути доступа к атрибуту объекта:
a = {
b: {
c: 'd'
}
}
Здесь путь доступа к 'd' — это abc, который после синтаксического анализа равен ['a', 'b', 'c']. Например, выражение a[0].b.c анализируется как ['a', '0', 'b', 'c']. Выражение a[b][c] анализируется как ['a', '*b', '*c']. Цель синтаксического анализа — выполнить compileGetter, функцию-получатель; Причина разбора в массив состоит в том, что удобно восстанавливать правильную строку в новой конструкции Function().
exports.compileGetter = function (path) {
var body = 'return o' + path.map(formatAccessor).join('')
return new Function('o', body)
}
function formatAccessor(key) {
if (identRE.test(key)) { // identifier
return '.' + key
} else if (+key === key >>> 0) { // bracket index
return '[' + key + ']'
} else if (key.charAt(0) === '*') {
return '[o' + formatAccessor(key.slice(1)) + ']'
} else { // bracket string
return '["' + key.replace(/"/g, '\\"') + '"]'
}
}
Например, выражение:
<p>{{list[0].text}}</p>
解析后path为["list", "0", "text"], getter函数的生成结果为:
(function (o/**/) {
return o.list[0].text
})
Передайте правильный контекст в эту функцию, чтобы получить правильное значение. Vue.js будет использовать состояние состояния для сопоставления только тогда, когда строка пути содержит [Символ; в других случаях, он считается SimplePath, например, A.B.C, и вышеуказанное преобразование Formataccessor можно использовать напрямую. Государственный автомат работает следующим образом:
Логика внутри более сложная и может быть просто описана так:
- Vue.js поддерживает набор механизмов конечного автомата, каждый проанализированный символ соответствует соответствующему состоянию;
- Если текущий индекс символа равен 0, то существует режим «текущего состояния», который позволяет только следующему символу принадлежать определенному режиму состояния. Например, такие выражения, как ][ явно неразумны.Состояние символа «]» определяет, что следующим символом не может быть такой, как «[»
- символ, иначе парсинг прекратится;
- Следующий индекс должен увидеть, принадлежит ли он разумному состоянию в соответствии с режимом «текущего состояния»;
- Если он принадлежит к разумному состоянию, сначала установите режим текущего состояния в режим состояния текущего соответствия символов;
- Затем позвоните в соответствующий метод (действие) для обработки. Например, список выражений [0], при обработке «L», «I», «S», «T», просто выполнить Append Action, генерировать «список»; до встречи «[», выполнить нажимное действие, толкает «Список» строка в результате.
Конечный автомат Vue.js можно увидеть на этой диаграмме, представленной тремя четвертями.
кеш-система
Представьте себе сценарий: когда путей (путей) для разбора много, то, скорее всего, будет много повторений. Как упоминалось выше, синтаксический анализ конечного автомата — утомительный процесс. Затем необходима система кэширования.Как только выражение пути попадает в кеш, его можно получить напрямую, без повторного разбора. При проектировании системы кэширования следует учитывать следующие моменты:
Кэшированные данные должны быть ограничены. В противном случае легко переполнить память из-за слишком большого количества данных. Устанавливать количество хранимых данных следует исходя из реальной ситуации, полученной при тестировании. Когда кешированные данные достигают верхнего предела, если данные продолжают храниться, должна быть соответствующая стратегия для очистки существующего кеша. Vue.js использует проект js-lru непосредственно в системе кэширования. Это реализация алгоритма LRU (наименее недавно использованный). Основная идея заключается в следующем:
Базовая структура данных — это двусвязный список, реализованный с помощью js. Объект кэша имеет начало и хвост, то есть начало (наименее используемый элемент) и хвост (элемент, который использовался последним). Каждый элемент в кеше имеет новые и старые указатели. Чтобы найти данные в кеше, используйте ключ объекта, чтобы найти их. Конкретная реализация выглядит следующим образом:
По схеме очень просто понять:
- Получить элемент кэша B, вставить B в хвост, D и B установить более новую, старую связь, а A и C установить более новую, старую связь;
- Установить элемент кеша E, вставить E в хвост, D и E установить более новую, старую связь;
- Когда достигается верхний предел кеша, запись кеша A (заголовок) удаляется, а B становится заголовком.
Чтобы узнать о других реализациях системы кэширования, см. Политики замены кэша в Википедии. Коллекция зависимостей Вернемся к конструктору Watcher:
function Watcher(vm, expOrFn, cb, options) {
//...
// 解析表达式,得到getter和setter函数
var res = parseExpression(arguments)
this.getter = res.get
this.setter = res.get
// 设定Dep.target为当前Watcher实例
Dep.target = this
// 调用getter函数
try {
value = this.getter.call(scope, scope)
} catch (e) {
//...
}
}
В чем здесь тайна? Просмотрите defineReactive наблюдателя:
function defineReactive(obj, key, val) {
var dep = new Dep()
var childOb = Observer.create(val)
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function metaGetter() {
// 如果Dep.target存在,则进行依赖收集
if (Dep.target) {
dep.depend()
if (childOb) {
childOb.dep.depend()
}
}
return val
},
set: function metaSetter(newVal) {
if (newVal === val) return
val = newVal
childOb = Observer.create(newVal)
dep.notify()
}
})
}
Видно, что Watcher устанавливает Dep.target в качестве текущего экземпляра Watcher и активно вызывает геттер, после чего он неизбежно войдет в функцию dep.depend(). dep.depend() фактически выполняет Watcher.addDep() :
Watcher.prototype.addDep = function (dep) {
var id = dep.id
if (!this.newDeps[id]) {
this.newDeps[id] = dep
if (!this.deps[id]) {
this.deps[id] = dep
dep.addSub(this)
}
}
}
Видно, что Watcher устанавливает dep как зависимость текущего экземпляра, а dep устанавливает (добавляет) текущий Watcher в качестве подписчика. На этом сбор зависимостей завершен. Из функции установки в defineReactive выше также можно узнать, что когда данные изменяются, Dep уведомляет об этом (dep.notify()), обходит всех подписчиков (Watcher), помещает их в асинхронную очередь и использует метод обновления подписчик, обновляйте DOM партиями. На этом работа Наблюдателя завершена.
- Фактически, механизм сбора зависимостей Watcher также является основой для реализации вычисляемых свойств; ядром является перехват геттеров, инициирование уведомлений и сбор зависимостей.
- На заре Vue.js от разработчиков требовалось устанавливать метод получения для вычисляемого свойства, и это делалось непосредственно в вычисляемом свойстве на более позднем этапе, что было очень удобно для разработчиков.
- Рекомендуется прочитать эту статью: Ассоциативные вычисления данных.
Другая черная магия Vue.js
Из-за нехватки места содержание, обсуждаемое в этой статье, в основном относится к основным модулям Observer, Compiler и Watcher, но на самом деле в исходном коде Vue.js (или историческом исходном коде) есть большое количество других отличных реализаций. ), Такие как:
- асинхронная очередь дозатора
- Алгоритм сравнения DOM в v-for
- Парсер exp когда-то позаимствовал шаблонный движок artTemplate
- парсер шаблонов заимствует jquery
- конструкция переходной системы
и т.п. Другие способы разделения кода, проектирования, тестовых случаев и т. д. также являются очень хорошими примерами для обучения. Кроме того, если вы являетесь студентом, знакомым с процессом эволюции исходного кода Vue.js, вы обнаружите, что основная идея Vue.js (если заимствовать ваше собственное выражение):
«Сделать высокие умы доступными»
От концепции фреймворка, опыта разработки, дизайна API, дизайна семейного сегмента и других аспектов Vue.js постоянно стремится к удобству и простоте, поэтому он так популярен сейчас.
Как читать исходный код проекта с открытым исходным кодом?
Наконец, я хотел бы поделиться со студентами некоторым опытом чтения исходного кода:
- Не смотрите на последнюю версию реализации, как только вы столкнетесь с ней, потому что ее сложно понять. Вместо этого легко понять основную концепцию автора, взглянув на первоначальную реализацию.
- Перейти к исходному коду с вопросами и тестовыми примерами.
- Используйте больше инструментов отладки, запускайте различные тестовые процессы, и вы сможете понять это, просто взглянув на него... если только вы не компилятор человеческой плоти.
- Найдите одного (или нескольких) небольших партнеров, чтобы посмотреть исходный код вместе с вами, больше общаться, и эффект будет намного лучше, чем у одного человека.
- Настойчивость, если не продолжать читать, легко забыть и потерять интерес к обучению.
- Постоянно резюмируйте, хорошая память не так хороша, как плохое письмо, и те знания, которые действительно суммируются из вас самих, — это те знания, которые вы впитываете.
Выше поделитесь.